Feature flag abuse
Theory
Why This Matters
In 2021, a researcher disclosed that a major SaaS platform's beta program stored enrollment state in a cookie: beta_user=true. Setting this cookie on a non-beta account granted access to an unreleased AI analytics dashboard that had not yet undergone security review and contained several unauthenticated API endpoints that the security team was unaware were live. Separately, a penetration test of an enterprise collaboration tool found that changing a plan parameter in the session JWT from starter to enterprise unlocked bulk export and SSO configuration features that the server enforced only through UI visibility, not through API-level authorization. These incidents illustrate the core risk of client-side feature flag evaluation: when the flag is toggled by the client, all downstream logic that trusts the flag is trivially bypassed.
Core Concept
A feature flag (also called a feature toggle or feature switch) is a conditional that enables or disables application functionality at runtime without a code deployment. Feature flags are used legitimately for A/B testing, canary releases, dark-launched features, and plan-based entitlements. The vulnerability arises when the flag's value is read from a client-controlled source — a cookie, a URL parameter, a request header, or the body of an API request — rather than being evaluated authoritatively on the server.
Client-side flag sources include: browser cookies (beta=true), localStorage values mirrored to API requests, query parameters (?plan=enterprise&debug=1), HTTP request headers (X-Feature-Beta: enabled), and JWT claims ({"plan": "enterprise"}). When the server trusts these values without independent verification, any client can assert any flag value.
Dark-launched endpoints are a related risk: a feature that is not yet publicly visible may still be routed and handled by the server. Even without knowing the flag, an attacker who discovers the endpoint URL (through JavaScript bundle analysis, error messages, or directory fuzzing) can access the feature directly.
Server-side flag evaluation — using a system like LaunchDarkly, Unleash, or a database-backed flag store — means the server calls flags.isEnabled('beta_dashboard', user_id) using the authenticated user_id as the evaluation context. The client never supplies the flag value; it is derived entirely from server-side data.
Technical Deep-Dive
# Discovery: inspect cookies on a beta account vs. standard account
# Beta account cookie:
Cookie: session=jwt_abc; plan=enterprise; beta_user=true; debug_mode=false
# Standard account cookie:
Cookie: session=jwt_xyz; plan=starter; beta_user=false; debug_mode=false
# Attack: modify standard account cookies to match beta account values
Cookie: session=jwt_xyz; plan=enterprise; beta_user=true; debug_mode=true
# Observe: does the server now render beta UI elements?
# Does it grant access to enterprise API endpoints?
# Test API directly — send plan claim in request body
POST /api/export/bulk HTTP/1.1
Host: app.example.com
Cookie: session=jwt_xyz
Content-Type: application/json
{"format": "csv", "plan": "enterprise"}
# Vulnerable response: begins bulk export (server trusted request-body plan field)
# Secure response: 403 Forbidden (server evaluated plan from authenticated session record)
// JavaScript bundle analysis — look for dark-launched route definitions
// Search minified JS for feature flag keys and hidden routes
// grep equivalent in DevTools console:
// Find all fetch/XHR calls mentioning 'beta', 'admin', 'internal', 'debug'
const scripts = document.querySelectorAll('script[src]');
// Then fetch each src and search for flag names:
// /beta/, /plan.*enterprise/, /feature.*admin/, /debug.*true/
// Common flag storage patterns to test:
localStorage.setItem('featureFlags', JSON.stringify({beta: true, adminPanel: true}));
// Reload page and observe whether admin routes become accessible
# Systematic flag parameter fuzzing with Burp Intruder wordlist
# Common flag names to inject as query parameters, headers, and body keys
FLAG_NAMES = [
'beta', 'beta_user', 'is_beta', 'preview',
'plan', 'tier', 'subscription', 'role',
'admin', 'is_admin', 'staff', 'internal',
'debug', 'dev', 'test', 'staging',
'feature', 'flag', 'toggle', 'experiment'
]
FLAG_VALUES = ['true', '1', 'enterprise', 'admin', 'staff', 'premium']
Security Assessment Methodology
- Compare cookie sets across account tiers — Create both a free and a paid/beta account. Compare all cookie names and values. Identify any flag-like fields (
plan,beta,role,tier). - Modify flag values in cookies — Use Burp Suite's Cookie editor or browser DevTools to change flag values on the lower-privilege session. Navigate the application and observe changes in available functionality.
- Inject flag parameters in API requests — Add flag-like keys (
plan=enterprise,beta=true,is_staff=1) to POST/PUT/PATCH request bodies. Test both JSON body injection and query parameter injection. - Analyse the JavaScript bundle for hidden routes — Download the main JS bundle. Search for route definitions, API endpoint strings, and feature flag key names. Attempt to directly request any dark-launched endpoints found.
- Test HTTP headers — Add custom headers:
X-Beta-Access: true,X-Plan: enterprise,X-Feature-Debug: 1. Some frameworks expose flag values through custom headers for internal proxies. - Decode and modify JWT claims — If the session uses a JWT, decode the payload. Look for plan, role, or feature claims. Test HS256
nonealgorithm bypass and weak secret brute-force (jwt_tool).
Defensive Countermeasure — Evaluate all feature flags server-side using the authenticated user identity as the sole context. Use a dedicated flag evaluation service (LaunchDarkly server-side SDK, Unleash server-side client, or a database-backed flag store) that is not accessible to clients. Never read flag state from cookies, request bodies, query parameters, or JWT claims that the client can modify. For JWT-based plans, verify the plan claim against the authoritative subscription database on every privileged request, not just at token issuance. Apply route-level middleware to dark-launched endpoints that returns 404 or 403 until the feature is officially enabled for the requesting user.
Common Assessment Errors
- Testing only cookie-based flags — Feature flags also appear in JWT claims, localStorage, API request bodies, and custom HTTP headers. Always test all four surfaces.
- Ignoring the dark-launched endpoint surface — A feature may be unreachable via the UI but fully functional via direct API call. JavaScript bundle analysis frequently reveals these routes.
- Assuming plan downgrade is the only risk — Flag abuse can also enable debug modes, disable rate limiting (
debug=true), expose internal diagnostics, or enable export features with insecure implementations. - Missing flag evaluation in API responses — Some applications return the flag state in API responses and then conditionally render UI elements client-side based on that response. The API may expose all data regardless of the flag — the flag only controls visibility.
- Not testing after flag propagation delay — Feature flag systems sometimes cache flag values. A modified flag may not take effect immediately. Test after a short delay and after re-authenticating.
- Overlooking localStorage-to-request mirroring — Some SPAs read feature flags from localStorage and include them in API request headers or bodies for telemetry. Modifying localStorage values may directly influence server-side behaviour.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0007 | Knowledge of authentication, authorization, and access control methods | Demonstrates that feature flag enforcement is an authorization control that must be server-side |
| K0065 | Knowledge of web application security testing techniques | Develops cookie, JWT, and bundle analysis techniques for feature flag discovery |
| K0070 | Knowledge of system and application security threats and vulnerabilities | Identifies client-side flag evaluation as a systemic architectural vulnerability pattern |
| S0001 | Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems | Trains multi-surface flag injection testing across cookies, headers, and request bodies |
| T0028 | Identify and analyze vulnerabilities and risks in web applications | Applies trust boundary analysis to identify client-controlled flag evaluation paths |
| T0570 | Perform technical security assessments of web applications | Structures feature flag assessment from account comparison through dark-launched endpoint access |
Further Reading
- Fowler, M. (2017). Feature Toggles (aka Feature Flags) — martinfowler.com
- OWASP Testing Guide v4.2, OTG-AUTHZ-002: Testing for Bypassing Authorization Schema — OWASP Foundation
- LaunchDarkly Server-Side SDK Security Model — LaunchDarkly Documentation
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.