Jwt Misconfiguration in Flask with Bearer Tokens
Jwt Misconfiguration in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
JWT misconfiguration in Flask APIs that use Bearer Tokens commonly arises when token validation is incomplete or overly permissive. Flask itself does not enforce JWT handling; developers typically rely on libraries such as PyJWT and integrations like Flask-JWT-Extended or custom decorators. Misconfiguration can occur at multiple levels: algorithm confusion, weak key material, missing audience/issuer validation, lack of token binding, and improper error handling.
One prevalent pattern is accepting Bearer Tokens exclusively via the Authorization header but failing to enforce strict token parsing. For example, if a route extracts the token with a simple split on Bearer and does not validate the token signature, an attacker can supply an unsigned or algorithm-swapped token. If the server falls back to none algorithm or uses a symmetric secret where an asymmetric key is expected, an attacker can forge tokens and impersonate any user without knowing the private key.
Insecure default settings in Flask-JWT-Extended can exacerbate the issue. If JWT_ALGORITHM is not explicitly set to a strong hash like HS256 or RS256, or if token expiration is not enforced, stolen tokens remain valid indefinitely. Missing audience (aud) and issuer (iss) checks allow tokens issued for one service to be accepted by another, especially in microservice environments where APIs share a common authorization layer but do not validate intended recipients. Absence of token binding or nonce claims enables replay attacks where intercepted Bearer Tokens are reused.
Another subtle flaw is leaking information through error messages. Inconsistent validation paths can disclose whether a token is malformed, expired, or signature invalid, aiding account enumeration. When CORS or proxy configurations forward the Authorization header incorrectly, or when tokens are logged inadvertently, Bearer Tokens may be exposed in logs or browser JavaScript contexts, increasing theft risk. These misconfigurations align with common weaknesses listed in the OWASP API Security Top 10, particularly Broken Object Level Authorization (BOLA) and Security Misconfiguration, and can be discovered by scanners that correlate spec definitions with runtime behavior.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
Remediation focuses on strict validation, explicit configuration, and defense in depth for Bearer Tokens in Flask. Always specify and enforce the signing algorithm, validate standard claims, and avoid fallback logic that accepts unsigned tokens.
Example: Secure token verification with PyJWT
Instead of relying on framework magic, validate tokens explicitly using PyJWT. This makes validation logic visible and auditable.
import jwt
from flask import request, jsonify, current_app
def verify_bearer_token(req):
auth = req.headers.get('Authorization')
if not auth or not auth.startswith('Bearer '):
return None
token = auth.split(' ')[1]
try:
# Explicitly set algorithm and expected claims
decoded = jwt.decode(
token,
key=current_app.config['JWT_PUBLIC_KEY'],
algorithms=['RS256'],
audience='api.example.com',
issuer='auth.example.com',
options={'require': ['exp', 'iss', 'aud']}
)
return decoded
except jwt.ExpiredSignatureError:
return 'expired'
except jwt.InvalidTokenError:
return 'invalid'
@app.route('/profile')
def profile():
claims = verify_bearer_token(request)
if claims is None:
return jsonify(error='Authorization header required'), 401
if claims == 'expired':
return jsonify(error='Token expired'), 401
if claims == 'invalid':
return jsonify(error='Invalid token'), 401
return jsonify(user=claims['sub'])
Example: Flask-JWT-Extended with strict settings
When using Flask-JWT-Extended, configure it to reject weak algorithms and enforce token binding.
from flask import Flask from flask_jwt_extended import JWTManager, jwt_required, get_jwt_identity app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'super-secret-key' # Use env var in prod app.config['JWT_ALGORITHM'] = 'HS256' app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=15) app.config['JWT_DECODE_ALGORITHMS'] = ['HS256'] app.config['JWT_LEEWAY'] = 0 app.config['JWT_DECODE_AUDIENCE'] = 'api.example.com' app.config['JWT_DECODE_ISSUER'] = 'auth.example.com' jwt = JWTManager(app) @app.route('/items') @jwt_required() def list_items(): current_user = get_jwt_identity() return jsonify(items=[], user=current_user)Operational and spec-level practices
- Use asymmetric keys (RS256) for distributed systems; keep private keys in a secure vault and publish public keys via JWKS endpoint.
- Validate
audandisson every request to prevent token misuse across services. - Set short expiration times and use refresh tokens with strict rotation policies.
- Ensure CORS does not inadvertently expose the Authorization header to unauthorized origins.
- Standardize error responses to avoid leaking validation details; return a generic 401 for malformed, expired, or invalid tokens.
These practices reduce the likelihood of JWT misconfiguration and help the scanner correlate spec-defined security expectations with runtime behavior, surfacing findings related to authentication weaknesses and insecure configuration.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |