HIGH privilege escalationflaskbearer tokens

Privilege Escalation in Flask with Bearer Tokens

Privilege Escalation in Flask with Bearer Tokens

Privilege Escalation occurs when a user gains unauthorized access to resources or actions reserved for higher-privilege roles. In Flask applications using Bearer Tokens for authentication, this risk emerges from how tokens are issued, validated, and bound to authorization scopes. If token validation is incomplete or authorization checks are missing, an attacker can modify the token’s claims (for example, changing a role claim from user to admin) or reuse a token issued for a lower-privilege scope to access higher-privilege endpoints.

Flask itself does not enforce authorization; it relies on developers to implement checks after authentication. When Bearer Tokens are used, common pitfalls include: accepting tokens without verifying signatures or audiences, failing to validate scope/role claims on each request, and using global permissions instead of per-resource authorization. An attacker who obtains a valid Bearer Token (via leakage, insecure storage, or a compromised client) can attempt horizontal privilege escalation by altering token claims if the server is permissive, or vertical privilege escalation by leveraging weak route-level guards to access admin-only routes.

Consider a Flask route that reads a token from the Authorization header but only checks presence, not content:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/api/admin/settings")
def admin_settings():
    auth = request.headers.get("Authorization")
    if auth and auth.startswith("Bearer "):
        # Vulnerable: no validation of token content/scopes/roles
        return jsonify({"status": "admin settings"})
    return jsonify({"error": "unauthorized"}), 401

In this example, any Bearer Token is accepted, enabling privilege escalation if an attacker can guess or reuse a token. Even when using a library like PyJWT to decode tokens, omitting scope/role validation leads to the same issue:

import jwt
from flask import request, jsonify

SECRET = "supersecret"

@app.route("/api/user/profile")
def user_profile():
    token = request.headers.get("Authorization", "").replace("Bearer ", "")
    try:
        payload = jwt.decode(token, SECRET, algorithms=["HS256"])
        # Vulnerable: missing checks for 'scope' or 'role' claims
        return jsonify(payload)
    except jwt.PyJWTError:
        return jsonify({"error": "invalid token"}), 401

Without validating claims such as scope or role, an attacker can present a token issued for read-only access to perform write or admin actions. Additionally, if token binding is weak (e.g., no checks on token issuer or audience), tokens issued for one service may be accepted by another, further enabling escalation. Proper defense combines strong token validation, strict scope/role enforcement, and per-resource authorization checks.

Bearer Tokens-Specific Remediation in Flask

Remediation focuses on strict token validation, scope/role enforcement, and minimizing trust in client-supplied claims. Always verify the token signature, issuer, audience, and expiration. Then enforce scope or role claims on every request, using a centralized authorization helper rather than repeating checks across routes.

Example: validate JWT Bearer tokens and enforce scope/role claims in Flask:

import jwt
from functools import wraps
from flask import request, jsonify

def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.headers.get("Authorization")
        if not auth or not auth.startswith("Bearer "):
            return jsonify({"error": "missing token"}), 401
        token = auth.split(" ")[1]
        try:
            payload = jwt.decode(
                token,
                "your-public-key-or-secret",
                algorithms=["RS256"],
                options={"require": ["exp", "iss", "aud", "scope"]},
                issuer="https://auth.example.com/",
                audience="myapi.example.com",
            )
            # Attach claims for downstream use
            request.token_payload = payload
        except jwt.ExpiredSignatureError:
            return jsonify({"error": "token expired"}), 401
        except jwt.InvalidTokenError:
            return jsonify({"error": "invalid token"}), 401
        return f(*args, **kwargs)
    return decorated

def require_scope(required_scope):
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            if not hasattr(request, "token_payload"):
                return jsonify({"error": "unauthorized"}), 401
            scopes = request.token_payload.get("scope", "").split()
            if required_scope not in scopes:
                return jsonify({"error": "insufficient scope"}), 403
            return f(*args, **kwargs)
        return decorated
    return decorator

@app.route("/api/admin/settings")
@token_required
@require_scope("admin:settings")
def admin_settings():
    return jsonify({"status": "admin settings"})

@app.route("/api/user/profile")
@token_required
@require_scope("profile:read")
def user_profile():
    return jsonify(request.token_payload)

Key practices:

  • Verify signature with the appropriate key (symmetric or asymmetric).
  • Validate standard claims: iss (issuer), aud (audience), exp (expiration), and nbf (not before).
  • Enforce scope or role claims on each endpoint; prefer scope-based authorization for fine-grained control.
  • Use centralized decorators or a policy engine to avoid duplicated checks.
  • Store tokens securely on the client side and rotate signing keys regularly.

These steps reduce the likelihood of privilege escalation via Bearer Tokens by ensuring tokens are trustworthy and permissions are checked consistently.

Frequently Asked Questions

How can I test if my Flask Bearer Token implementation is vulnerable to privilege escalation?
Use a Bearer Token with low-privilege claims and attempt to access admin-only routes; also inspect whether token signature, issuer, audience, and scope/role claims are validated on every request. middleBrick can scan your endpoint to detect missing authorization checks and token validation issues.
Does using the Flask-JWT extension automatically protect against privilege escalation?
Not by itself. Flask-JWT helps with token parsing and signature verification, but you must explicitly validate issuer, audience, expiration, and scope/role claims and enforce per-route authorization. middleBrick’s scans include checks for missing scope/role enforcement to highlight gaps.