HIGH man in the middleflaskbearer tokens

Man In The Middle in Flask with Bearer Tokens

Man In The Middle in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability

A Man In The Middle (MitM) scenario in a Flask application that uses Bearer Tokens occurs when an attacker can intercept or tamper with HTTP traffic between the client and the server. In this context, the token itself becomes the object of interception or manipulation, and the trust placed in the Bearer scheme amplifies the impact because any party presenting a valid token is accepted by the API as the associated identity.

Flask APIs commonly rely on HTTPS for transport security, but misconfiguration or lack of strict enforcement can leave endpoints vulnerable. For example, if a Flask route uses a non-HTTPS (HTTP) endpoint or if TLS is terminated at a load balancer without strict validation, an attacker on the same network can potentially downgrade or intercept requests. Once intercepted, the attacker can capture the Authorization header containing the Bearer token and reuse it (replay) to impersonate the victim.

Another specific risk arises when Flask applications parse and forward requests (for example in backend-for-frontend or proxy patterns) without adequately validating the Host header or without binding the token usage to the original request context. This can enable an attacker to inject or alter the Authorization header as the request traverses intermediaries. Because Bearer tokens are often stored in browser local storage or in mobile app insecure storage, they can also be exposed via client-side vulnerabilities that facilitate interception.

The combination of Flask, which provides flexibility but minimal opinionated security defaults, and Bearer Tokens, which rely entirely on the confidentiality and integrity of the transmission, means that implementation choices have outsized effects. Common weaknesses include missing HTTP Strict Transport Security (HSTS), missing token binding, missing audience/issuer validation in introspection logic, and failure to use the Secure and HttpOnly flags for cookies that carry tokens. These gaps allow an attacker to observe, replay, or alter tokens in transit, effectively compromising authentication without needing to crack the token itself.

Bearer Tokens-Specific Remediation in Flask — concrete code fixes

Remediation focuses on ensuring token confidentiality in transit, strict validation, and minimizing the window for token misuse. Below are concrete, secure patterns for Flask applications that use Bearer Tokens.

1. Enforce HTTPS and HSTS

Ensure all API endpoints are served over HTTPS and that HSTS is enabled to prevent protocol downgrade attacks.

from flask import Flask
from flask_talisman import Talisman

app = Flask(__name__)
# Enforce HTTPS and set HSTS to prevent downgrade attacks
Talisman(app, content_security_policy=None, force_https=True, include_subdomains=True, preload=True, max_age=31536000)

2. Validate Authorization header format and presence

Check that the Authorization header is present and follows the "Bearer " scheme before processing the request.

import re
from flask import request, jsonify

def get_bearer_token():
    auth = request.headers.get('Authorization', '')
    # Ensure correct Bearer schema
    match = re.match(r'^Bearer\s+(\S+)$', auth)
    if not match:
        return None
    token = match.group(1)
    # Basic sanity checks: non-empty, no whitespace
    if not token or re.search(r'\s', token):
        return None
    return token

@app.route('/profile')
def profile():
    token = get_bearer_token()
    if token is None:
        return jsonify({'error': 'Unauthorized'}), 401
    # Continue with token validation (introspection/jwks verification)
    return jsonify({'user': 'alice', 'scope': 'read'}), 200

3. Verify token signatures and claims (JWKS example)

When using JWT Bearer tokens, validate the signature against a trusted JWKS endpoint and verify standard claims such as issuer (iss), audience (aud), and expiration (exp).

import jwt
import requests
from jwt import PyJWKClient
from flask import request, jsonify

def verify_token(token: str) -> dict:
    jwks_url = 'https://auth.example.com/.well-known/jwks.json'
    jwks_client = PyJWKClient(jwks_url)
    signing_key = jwks_client.get_signing_key_from_jwt(token)
    decoded = jwt.decode(
        token,
        key=signing_key.key,
        algorithms=['RS256'],
        options={'require': ['exp', 'iss', 'aud']},
        audience='https://api.example.com',
        issuer='https://auth.example.com/',
        leeway=10
    )
    return decoded

@app.route('/items')
def list_items():
    auth = request.headers.get('Authorization', '')
    match = re.match(r'^Bearer\s+(\S+)$', auth)
    if not match:
        return jsonify({'error': 'Unauthorized'}), 401
    try:
        claims = verify_token(match.group(1))
    except Exception:
        return jsonify({'error': 'Invalid token'}), 401
    return jsonify({'data': 'secure-data', 'user': claims.get('sub')}), 200

4. Avoid storing tokens in local storage; prefer secure cookies for web clients

If your frontend is a browser-based app, avoid embedding Bearer tokens in JavaScript-accessible storage. Instead, use HttpOnly, Secure cookies and configure CORS strictly.

from flask import Flask, request, make_response

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
    # After validating credentials
    response = make_response({'message': 'ok'})
    response.set_cookie(
        'access_token',
        value='your-jwt-here',
        httponly=True,
        secure=True,
        samesite='Lax',
        max_age=3600
    )
    return response

5. Implement short token lifetimes and secure refresh flows

Use short-lived access tokens and validate refresh token rotation and binding to mitigate replay should a token be intercepted.

import time
from flask import jsonify

def is_token_expired(payload: dict) -> bool:
    # payload['exp'] is Unix timestamp
    return payload.get('exp', 0) < (time.time() + 10)

@app.route('/token/refresh', methods=['POST'])
def refresh():
    refresh_token = request.json.get('refresh_token')
    # Validate refresh token binding, revocation, and rotation here
    if not is_valid_refresh_token(refresh_token):
        return jsonify({'error': 'invalid_grant'}), 400
    new_access = create_new_access_token(refresh_token)
    return jsonify({'access_token': new_access, 'expires_in': 300})

6. Use TLS with strong ciphers and certificate pinning for sensitive clients

Ensure your server presents a valid certificate and consider certificate or public-key pinning for high-risk clients to reduce the risk of a compromised CA enabling MitM.

Frequently Asked Questions

Does middleBrick test for Man In The Middle risks in Flask APIs that use Bearer Tokens?
Yes. middleBrick runs unauthenticated scans that include checks for transport security issues such as missing HTTPS enforcement and checks for insecure handling of Bearer Tokens, helping identify MitM-related exposure.
Can the OWASP API Top 10 mapping in middleBrick help prioritize Bearer Token MitM findings?
Yes. Findings map to frameworks such as OWASP API Top 10, so MitM and broken authentication related findings are prioritized with severity and remediation guidance you can act on.