flawopen.com/bola-idor/Python
Éliminez les failles BOLA (CWE-639) et IDOR en Python et FastAPI en liant strictement les requêtes de base de données à l'organisation de l'utilisateur authentifié.
Une clé d'hôtel pour la chambre 204 qui ouvre la 205 si l'on modifie le numéro sur la porte. L'hôtel a vérifié votre identité de client, mais pas votre droit d'accès à cette chambre précise.
Authentication vs. AuthorizationBOLA (Broken Object Level Authorization)IDOR (Insecure Direct Object Reference)CWE-639) où les objets d'implémentation interne sont directement exposés aux utilisateurs sans contrôle d'accès (IDOR).Tenancy Scope InjectionNon-Enumerable Identifiers (UUIDv4)An attacker creates a legitimate account on the platform and receives a valid JWT authentication bearer token.
The attacker accesses their own billing invoice via GET /api/invoices/1042 and observes sequential database identifiers in use.
The attacker modifies the URL to request GET /api/invoices/1041 using their own valid authentication token.
The FastAPI backend confirms the token is valid, but queries the database solely by Invoice.id == 1041 without verifying tenant ownership.
The server returns confidential invoice records, billing details, and personal data belonging to another organization.
The attacker scripts a loop iterating over IDs 1 through 100,000, draining the entire multi-tenant database.
# VULNERABLE: Verifies user login, but queries object solely by client ID
from fastapi import FastAPI, Depends, HTTPException, status
from sqlalchemy.orm import Session
@app.get("/api/invoices/{invoice_id}")
def get_invoice(invoice_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
# Flaw: Attacker passes invoice_id belonging to another tenant
invoice = db.query(Invoice).filter(Invoice.id == invoice_id).first()
if not invoice:
raise HTTPException(status_code=404, detail="Invoice not found")
# Leaks confidential billing records of competitor organizations!
return invoice
# HARDENED: Query is strictly bound to the authenticated tenant's organization ID
from fastapi import FastAPI, Depends, HTTPException, status
from sqlalchemy.orm import Session
@app.get("/api/invoices/{invoice_id}")
def get_invoice(invoice_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
# Defense-in-depth: query filters by BOTH invoice_id AND authenticated organization_id
invoice = db.query(Invoice).filter(
Invoice.id == invoice_id,
Invoice.organization_id == current_user.organization_id
).first()
if not invoice:
# Return 404 rather than 403 to prevent object existence enumeration
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Invoice not found")
return invoice