Vulnerable Components in Flask with Bearer Tokens
Vulnerable Components in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Flask does not enforce authentication by default. When APIs use Bearer Tokens in the Authorization header, three common implementation gaps can expose endpoints: missing validation of the token format, accepting tokens over non-TLS channels, and treating bearer identity as authorization. Together, these gaps can lead to Broken Object Level Authorization (BOLA/IDOR), unsafe consumption of untrusted input, and excessive agency when token handling is coupled with dynamic routing or mass assignment.
1) Missing validation and case-sensitive token handling. If a route reads request.headers.get('Authorization') and only checks for a non-empty string, an attacker can supply any string (e.g., Bearer invalid) and still pass the check. Without verifying token format (e.g., expecting Bearer followed by a structured token), malformed or missing tokens may be accepted, effectively bypassing authentication.
2) Accepting Bearer tokens over HTTP. Transmitting tokens without TLS exposes the bearer credential to interception. Even if the token is validated, sending it in cleartext violates confidentiality and enables replay. APIs that expose unauthenticated attack surface endpoints while accepting bearer tokens over HTTP increase exposure of credentials and session tokens.
3) Treating bearer identity as authorization. A common pattern decodes a JWT from the Bearer token and uses claims such as sub to identify a user, but then uses that identifier directly in database queries without enforcing ownership checks. For example, an endpoint like /users/<user_id>/profile that trusts a decoded user_id from the token without verifying that the token’s subject matches the requested resource enables BOLA/IDOR. If the token is issued with excessive scopes or roles, and those are not validated per endpoint, it can lead to privilege escalation and unsafe consumption patterns.
These issues map to multiple checks in middleBrick’s 12 scans. Authentication checks verify whether endpoints require valid credentials and whether bearer tokens are properly validated. BOLA/IDOR tests confirm whether object-level access controls align with the authenticated identity derived from the token. Input Validation and Unsafe Consumption checks ensure that data from bearer contexts is not used to infer access to other resources or to execute unsafe operations. When combined, these gaps create a scenario where an API appears authenticated but is actually vulnerable to unauthorized access via predictable or missing authorization logic.
Example of a vulnerable Flask route that illustrates these gaps:
from flask import Flask, request, jsonify
app = Flask(__name__)
# Vulnerable: no real token validation, no HTTPS enforcement, no ownership check
@app.route('/api/users/<int:user_id>/profile')
def get_user_profile(user_id):
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return jsonify({'error': 'missing or invalid bearer token'}), 401
token = auth.split(' ', 1)[1]
# No verification of token signature or scope
# No check that the requesting subject matches user_id
return jsonify({'user_id': user_id, 'profile': 'data'})
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
Remediation centers on strict token validation, transport security, and explicit authorization checks. Use a library to verify JWTs, enforce HTTPS in production, and ensure the subject of the token matches the requested resource. Below are concrete, working examples that address each of the three gaps.
1) Validate Bearer token format and presence. Require the Authorization header to start with Bearer and extract the token safely. Optionally verify the token structure before proceeding.
from flask import Flask, request, jsonify
def get_bearer_token(req):
auth = req.headers.get('Authorization', '')
parts = auth.split()
if len(parts) != 2 or parts[0].lower() != 'bearer':
return None
return parts[1]
@app.route('/api/users/<int:user_id>/profile')
def get_user_profile(user_id):
token = get_bearer_token(request)
if token is None:
return jsonify({'error': 'invalid authorization header format'}), 401
# TODO: verify token signature and claims
return jsonify({'user_id': user_id, 'profile': 'data'})
2) Enforce HTTPS and reject tokens over HTTP. In production, ensure requests are served over TLS and reject cleartext token transmission. This example uses a before_request hook to block non-TLS in environments where TLS termination is expected.
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.before_request
def enforce_https():
if not request.is_secure:
return jsonify({'error': 'HTTPS required'}), 403
def get_bearer_token(req):
auth = req.headers.get('Authorization', '')
parts = auth.split()
if len(parts) != 2 or parts[0].lower() != 'bearer':
return None
return parts[1]
@app.route('/api/users/<int:user_id>/profile')
def get_user_profile(user_id):
token = get_bearer_token(request)
if token is None:
return jsonify({'error': 'invalid authorization header format'}), 401
# TODO: verify token signature and claims
return jsonify({'user_id': user_id, 'profile': 'data'})
3) Map token identity to resource ownership. Decode the JWT (using a verified library), extract the subject, and compare it with the requested user_id. This prevents BOLA/IDOR by ensuring the token’s subject matches the resource being accessed.
import jwt
from flask import Flask, request, jsonify
app = Flask(__name__)
SECRET_KEY = 'your-very-secret-key'
ALGORITHM = 'HS256'
def get_bearer_token(req):
auth = req.headers.get('Authorization', '')
parts = auth.split()
if len(parts) != 2 or parts[0].lower() != 'bearer':
return None
return parts[1]
def decode_token(token):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except jwt.InvalidTokenError:
return None
@app.route('/api/users/<int:user_id>/profile')
def get_user_profile(user_id):
token = get_bearer_token(request)
if token is None:
return jsonify({'error': 'invalid authorization header format'}), 401
payload = decode_token(token)
if payload is None:
return jsonify({'error': 'invalid token'}), 401
subject = payload.get('sub')
if subject is None or str(subject) != str(user_id):
return jsonify({'error': 'forbidden: token subject does not match resource'}), 403
return jsonify({'user_id': user_id, 'profile': 'data'})
Additional recommendations: rotate signing keys, set short token lifetimes, and scope tokens with claims that are validated per endpoint. For broader coverage, middleBrick’s scans can validate authentication mechanisms and flag endpoints where Bearer handling does not align with expected identity and authorization checks.