Open Redirect in Flask with Bearer Tokens
Open Redirect in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
An Open Redirect in Flask that involves Bearer Tokens typically occurs when a redirect response uses an untrusted value supplied via request input, query parameters, or headers without strict validation. A common pattern is a “next” or “redirect_to” parameter that the server uses to construct a Location header. If the value is reflected into the redirect without verifying that it belongs to the application’s trusted host list, an attacker can craft a URL that sends the victim to a malicious site.
When Bearer Tokens are involved, the risk surface expands because tokens are often transmitted in Authorization headers or embedded in URLs (for example, as query parameters). Consider a Flask route that accepts a token in the Authorization header and also uses a redirect parameter. If the server validates the token but does not validate the redirect target, an authenticated user (with a valid Bearer Token) can be coerced into following a link that silently sends their token and session context to an attacker. Attackers may combine this with social engineering, sending a link that appears to come from your domain and includes a valid-looking token, making the request seem legitimate.
In practice, this can manifest in APIs that return 302/307 responses with a Location derived from user input. For example, an OAuth-style authorization endpoint might accept a redirect_uri parameter and an access_token. Without strict allowlisting of redirect URIs, an attacker can supply a malicious redirect_uri while using a stolen or guessed Bearer Token, effectively chaining token exposure with phishing. Even if the token is not directly leaked in the redirect URL, the browser’s request will include cookies or other credentials associated with the domain, and the malicious site can capture the flow by observing the victim’s interaction or by registering a domain that closely resembles your own.
Real-world attack patterns include phishing pages that mimic your application’s look and feel, tricking users into clicking a link such as https://api.example.com/oauth/authorize?redirect_uri=https://evil.com/callback&token=eyJhbGciOi... If Flask does not validate redirect_uri against a strict allowlist and does not enforce same-site cookie attributes or referrer policies, the user’s browser may send cookies and the token in what appears to be a benign navigation flow. This becomes a chained vulnerability: token presence enables authorization, and open redirect enables exfiltration or luring users into malicious downstream actions.
To detect such issues during scanning, tools like middleBrick analyze unauthenticated and authenticated attack surfaces (where credentials are supplied) and correlate findings with the OpenAPI specification. For instance, if your spec defines an OAuth endpoint with a redirect_uri parameter but does not constrain it with a strict allowlist, middleBrick flags this as a potential Open Redirect. The scanner does not exploit or fix the issue but provides prioritized findings with severity, an OWASP API Top 10 mapping, and remediation guidance to help developers close the gap.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
Remediation focuses on strict validation of redirect locations and safe handling of Bearer Tokens. Below are concrete, realistic Flask patterns you can adopt.
1. Validate redirect URIs against an allowlist
Never trust user-supplied redirect URLs. Compare the parsed host against a strict list of allowed hosts or maintain a per-client allowlist. Here is a safe implementation:
from urllib.parse import urlparse, urljoin
from flask import request, redirect, abort
ALLOWED_REDIRECT_HOSTS = {"api.example.com", "app.example.com"}
def is_safe_url(url):
if not url:
return False
parsed = urlparse(url)
# Reject non-HTTP(S) schemes
if parsed.scheme not in {"http", "https"}:
return False
# Ensure host is in the allowlist
if parsed.hostname not in ALLOWED_REDIRECT_HOSTS:
return False
# Optionally enforce path safety
if parsed.path and "//" in parsed.path:
return False
return True
@app.route("/oauth/authorize", methods=["GET"])
def authorize():
redirect_uri = request.args.get("redirect_uri")
if not is_safe_url(redirect_uri):
abort(400, "Invalid redirect_uri")
# Perform Bearer token validation and OAuth logic here
return redirect(redirect_uri)
2. Avoid using user input in Location headers
If you must redirect, prefer server-side mappings instead of raw user input. For example, map short codes to safe paths:
@app.route("/redirect")
def safe_redirect():
code = request.args.get("code")
mapping = {
"success": "/dashboard",
"error": "/login?error=1"
}
target = mapping.get(code)
if target is None:
abort(400, "Invalid code")
return redirect(target)
3. Handle Bearer Tokens securely
Ensure tokens are transmitted and stored safely. When returning 302 responses, avoid placing tokens in URLs. If you must include tokens as query parameters (not recommended), ensure they are short-lived and scoped. Here is an example of extracting and validating a Bearer Token in Flask before processing a request that may issue redirects:
import jwt
from flask import request, jsonify
def get_bearer_token():
auth = request.headers.get("Authorization")
if auth and auth.startswith("Bearer "):
return auth.split(" ")[1]
return None
@app.before_request
def validate_token_for_protected_endpoints():
if request.endpoint in {"oauth_authorize", "public_info"}:
return
token = get_bearer_token()
if not token:
return jsonify({"error": "unauthorized"}), 401
try:
payload = jwt.decode(token, "your-public-key-or-secret", algorithms=["HS256"])
request.user = payload
except jwt.ExpiredSignatureError:
return jsonify({"error": "token expired"}), 401
except jwt.InvalidTokenError:
return jsonify({"error": "invalid token"}), 401
4. Use same-site cookies and referrer policies
Complementary defenses reduce the impact of a leaked token. Configure Flask to set cookie attributes and security headers:
@app.after_request
def set_security_headers(response):
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Permissions-Policy"] = "geolocation=()"
return response
@app.route("/login")
def login():
response = redirect("/")
response.set_cookie(
"session",
"token-value",
httponly=True,
samesite="Lax",
secure=True
)
return response
By combining allowlist-based redirect validation, safe token handling, and secure cookie policies, you mitigate Open Redirect risks even when Bearer Tokens are present. The middleBrick CLI can scan your Flask endpoints to surface misconfigurations; if you want continuous oversight, the Pro plan includes scheduled scans and GitHub Action integration to fail builds when risk thresholds are exceeded.