Jwt Misconfiguration in Flask
How Jwt Misconfiguration Manifests in Flask
Flask applications frequently mishandle JWT tokens in ways that create security vulnerabilities. The most common pattern involves improper token validation that allows attackers to bypass authentication entirely. Consider a Flask endpoint that decodes a JWT without verifying its signature:
from flask import Flask, request, jsonify
from jose import jwt
app = Flask(__name__)
SECRET_KEY = 'supersecretkey'
@app.route('/api/user')
def get_user():
token = request.headers.get('Authorization').split(' ')[1]
payload = jwt.decode(token, options={'verify_signature': False})
return jsonify({'user_id': payload['user_id']})
This code accepts any JWT token, even if it was forged or tampered with. An attacker can simply create a token with any user_id they want, and the application will trust it completely.
Another common Flask JWT misconfiguration involves weak secret keys. Flask developers sometimes use predictable secrets or store them in source control:
SECRET_KEY = os.environ.get('JWT_SECRET', 'fallback-default-secret')
If the environment variable isn't set, the application falls back to a known default, making it trivial for attackers to generate valid tokens.
Flask applications also frequently mishandle token expiration. Some implementations completely ignore the exp claim:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'], options={'verify_exp': False})
This allows attackers to use stolen tokens indefinitely. Additionally, Flask apps sometimes fail to validate the token's audience (aud) or issuer (iss) claims, making them vulnerable to token replay attacks across different services or environments.
Flask's extensibility can also introduce JWT misconfigurations through third-party libraries. For example, using Flask-JWT-Extended without proper configuration:
from flask_jwt_extended import JWTManager, create_access_token
jwt = JWTManager(app)
# Missing configuration for token location, expiration, etc.
Without explicitly setting configuration options like JWT_TOKEN_LOCATION or JWT_ACCESS_TOKEN_EXPIRES, the library uses defaults that may not match your security requirements.
Flask-Specific Detection
Detecting JWT misconfigurations in Flask applications requires examining both the code and runtime behavior. In your source code, look for these red flags:
- jwt.decode() calls without verify_signature=True
- Missing or weak SECRET_KEY values
- options={'verify_exp': False} in token decoding
- Missing audience (aud) or issuer (iss) validation
- Hardcoded secrets in configuration files
Runtime detection involves testing how your Flask application handles various JWT scenarios. A properly configured application should:
- Reject tokens with invalid signatures
- Reject expired tokens
- Reject tokens with mismatched audience or issuer
- Reject tokens using incorrect algorithms
middleBrick's scanner specifically tests these scenarios against your Flask endpoints. It attempts to bypass authentication by sending tokens with:
- Invalid signatures
- Expired timestamps
- Wrong audience claims
- None algorithm (alg: "none")
- HS256 tokens signed with the public key
The scanner also examines your OpenAPI spec if available, looking for JWT-related security definitions that don't match the actual implementation. For example, if your spec defines a bearer token scheme but the implementation doesn't validate tokens at all.
For Flask applications using Flask-JWT-Extended or similar libraries, middleBrick checks configuration completeness by examining the Flask app's configuration object for required JWT settings.
Code example of what to look for in Flask-JWT-Extended:
from flask import Flask
from flask_jwt_extended import JWTManager
app = Flask(__name__)
jwt = JWTManager(app)
# Check these configurations exist:
assert app.config.get('JWT_SECRET_KEY') is not None
assert app.config.get('JWT_ACCESS_TOKEN_EXPIRES') is not None
assert app.config.get('JWT_TOKEN_LOCATION') is not None
middleBrick's API security scanner can identify these misconfigurations automatically in about 5-15 seconds without requiring any credentials or setup. Just provide your Flask API endpoint URL and receive a detailed report with severity levels and remediation steps.
Flask-Specific Remediation
Fixing JWT misconfigurations in Flask requires both proper configuration and secure implementation patterns. Start with your Flask app configuration:
import os
from flask import Flask
from jose import jwt
app = Flask(__name__)
# Use strong, random secrets from environment variables
app.config['JWT_SECRET_KEY'] = os.environ['JWT_SECRET_KEY']
app.config['JWT_ALGORITHM'] = 'HS256'
app.config['JWT_EXPIRES_IN'] = 3600 # 1 hour
app.config['JWT_AUDIENCE'] = 'your-service-audience'
app.config['JWT_ISSUER'] = 'your-service-issuer'
# Create a strong secret if not set
if not app.config['JWT_SECRET_KEY']:
import secrets
app.config['JWT_SECRET_KEY'] = secrets.token_urlsafe(32)
For token validation, always verify the signature and all claims:
from jose.exceptions import JWTError
def validate_jwt(token):
try:
payload = jwt.decode(
token,
app.config['JWT_SECRET_KEY'],
algorithms=[app.config['JWT_ALGORITHM']],
audience=app.config['JWT_AUDIENCE'],
issuer=app.config['JWT_ISSUER'],
options={
'verify_signature': True,
'verify_exp': True,
'verify_aud': True,
'verify_iat': True,
'verify_nbf': True,
'verify_iss': True
}
)
return payload
except JWTError as e:
raise ValueError(f'Invalid token: {str(e)}')
For Flask applications using Flask-JWT-Extended, proper configuration looks like:
from flask import Flask
from flask_jwt_extended import JWTManager, jwt_required, get_jwt_identity
app = Flask(__name__)
app.config.update({
'JWT_SECRET_KEY': os.environ['JWT_SECRET_KEY'],
'JWT_ACCESS_TOKEN_EXPIRES': 3600,
'JWT_TOKEN_LOCATION': ['headers'],
'JWT_HEADER_NAME': 'Authorization',
'JWT_HEADER_TYPE': 'Bearer',
'JWT_AUDIENCE': 'your-service',
'JWT_ISSUER': 'your-service-issuer'
})
jwt = JWTManager(app)
@app.route('/api/protected')
@jwt_required()
def protected():
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
Always use HTTPS in production to prevent token interception. Store tokens securely on the client side (HTTP-only cookies or secure storage). Implement token rotation and revocation strategies for enhanced security.
middleBrick's scanner can verify your remediation by retesting the same JWT attack scenarios. After fixing your implementation, the scanner should show improved security scores and no critical findings related to JWT validation.
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 |