Insecure Direct Object Reference in Flask with Bearer Tokens
Insecure Direct Object Reference in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references (e.g., database IDs, filenames) without verifying that the requesting user has permission to access them. In Flask, this commonly happens when an endpoint uses a URL parameter such as /users/123/orders and directly uses the numeric ID to query a database or to construct a file path, relying only on the presence of a Bearer token for authentication but not for authorization.
Bearer tokens typically identify the caller (authentication) but do not encode authorization or tenant boundaries by themselves. If the Flask route uses the token to identify a user (e.g., via flask_jwt_extended or a custom middleware that sets g.user), but then fails to confirm that the resource being accessed belongs to that user, the endpoint becomes vulnerable. For example, an attacker who obtained a valid Bearer token for Alice might iterate through numeric IDs and access Bob’s records because the server never checks ownership or role-based access controls.
Consider a Flask route that lists invoices:
@app.route('/invoices/<int:invoice_id>', methods=['GET'])
@jwt_required()
def get_invoice(invoice_id):
# BOLA/IDOR: no check that the invoice belongs to the JWT identity
invoice = db.get_invoice_by_id(invoice_id)
if not invoice:
return jsonify({'error': 'not found'}), 404
return jsonify(invoice.serialize())
Even though a Bearer token is required, the token’s subject (e.g., sub claim) is never compared to the invoice’s user identifier. A low-privilege token can enumerate or access invoices across users, leading to mass data exposure. This pattern is common in APIs that expose sequential or guessable IDs, and it maps directly to OWASP API Top 10 A1: Broken Object Level Authorization.
In a black-box scan, middleBrick’s BOLA/IDOR checks send requests with different object IDs while using the same Bearer token, looking for unauthorized data access. If any response returns data that should be restricted, the scan flags a finding with severity High and provides remediation guidance. Real-world examples include accessing other tenants’ S3 keys via object paths or reaching healthcare records by incrementing patient IDs.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To fix BOLA/IDOR in Flask when using Bearer tokens, ensure that every data access validates the token’s subject (or associated claims) against the requested resource. The simplest and most secure approach is to derive resource ownership from the token rather than from user-supplied IDs wherever possible.
Example: include the user identifier in the token (e.g., sub or a custom claim like user_id), then use it to scope queries:
@app.route('/invoices/<int:invoice_id>', methods=['GET'])
@jwt_required()
def get_invoice(invoice_id):
current_user_id = get_jwt_identity() # or decode and read a custom claim
invoice = db.get_invoice_by_id_and_user(invoice_id, current_user_id)
if not invoice:
return jsonify({'error': 'not found'}), 404
return jsonify(invoice.serialize())
In this pattern, get_jwt_identity() extracts the subject from the validated Bearer token, and the data layer enforces ownership. If your schema does not store user IDs on each resource, introduce tenant or ownership columns and join them in queries to prevent cross-user access.
For endpoints where IDs should not be exposed, use opaque references (UUIDs) mapped server-side to internal keys, and validate mapping on each request:
import uuid
def map_public_to_internal(public_ref, user_id):
mapping = db.get_mapping(public_ref, user_id)
if not mapping:
return None
return mapping.internal_id
@app.route('/documents/<path:public_ref>', methods=['GET'])
@jwt_required()
def get_document(public_ref):
current_user_id = get_jwt_identity()
internal_id = map_public_to_internal(public_ref, current_user_id)
if internal_id is None:
return jsonify({'error': 'forbidden'}), 403
document = db.get_document_by_id(internal_id)
return jsonify(document.serialize())
Additional hardening steps include enforcing role-based access control (RBAC) checks, applying the principle of least privilege to token scopes, and using short-lived tokens with refresh rotation. middleBrick’s CLI can validate these fixes by scanning the same endpoint with different tokens and verifying that cross-user access is rejected. If you want to integrate checks into your workflow, the GitHub Action can add API security gates to your CI/CD pipeline, while the MCP Server lets you scan APIs directly from your AI coding assistant within your IDE.
For ongoing assurance, the Pro plan supports continuous monitoring with configurable schedules and alerts, so new IDOR regressions are detected before they reach production. Remember, middleBrick detects and reports these issues with prioritized findings and remediation guidance, but it does not fix or block requests; developers must apply the code changes based on the guidance provided.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |