flawopen.com/bola-idor/Python

● CWE-639 · Critique
Recherche · FlawOpen

Défaut d'Autorisation au Niveau de l'Objet (BOLA / IDOR) en 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é.

💡 Explication en Langage Simple (ELI5)

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.

Concepts Clés et Termes

Authentication vs. Authorization
L'authentification confirme QUI est l'utilisateur (connexion valide). L'autorisation confirme QUELLES ressources cet utilisateur spécifique a le droit de consulter ou modifier.
BOLA (Broken Object Level Authorization)
Vulnérabilité n°1 de l'OWASP API où un point de terminaison expose une référence d'objet (ex. : /api/invoices/1042) sans vérifier les droits du locataire (BOLA).
IDOR (Insecure Direct Object Reference)
Classe de vulnérabilité plus large (CWE-639) où les objets d'implémentation interne sont directement exposés aux utilisateurs sans contrôle d'accès (IDOR).
Tenancy Scope Injection
Modèle d'architecture où les requêtes de base de données appliquent automatiquement WHERE organization_id = :auth_user_org_id au niveau de l'ORM.
Non-Enumerable Identifiers (UUIDv4)
Utilisation d'identifiants 128 bits cryptographiquement aléatoires au lieu d'entiers auto-incrémentés séquentiels pour empêcher toute énumération prévisible.

Déroulement de l'Attaque Étape par Étape

Step 1

Legitimate Sign-In

An attacker creates a legitimate account on the platform and receives a valid JWT authentication bearer token.

Step 2

Resource Inspection

The attacker accesses their own billing invoice via GET /api/invoices/1042 and observes sequential database identifiers in use.

Step 3

Identifier Perturbation

The attacker modifies the URL to request GET /api/invoices/1041 using their own valid authentication token.

Step 4

Flawed Controller Logic

The FastAPI backend confirms the token is valid, but queries the database solely by Invoice.id == 1041 without verifying tenant ownership.

Step 5

Cross-Tenant Exfiltration

The server returns confidential invoice records, billing details, and personal data belonging to another organization.

Step 6

Automated Harvest

The attacker scripts a loop iterating over IDs 1 through 100,000, draining the entire multi-tenant database.

Code Source : Vulnérable vs Sécurisé

✕ IMPLÉMENTATION VULNÉRABLE
# 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
✓ PATCH SÉCURISÉ ET ROBUSTE
# 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

Liste de Contrôle de Sécurité pour l'Ingénierie

References