flawopen.com/bola-idor/Python
Elimine vulnerabilidades BOLA (CWE-639) e IDOR no FastAPI vinculando consultas de banco de dados diretamente à identidade e organização do usuário autenticado.
Imagine um hotel onde sua chave abre o quarto 204, mas ao raspar o número e escrever 205, ela abre o quarto do vizinho. O hotel checou que você é hóspede (autenticação), mas não validou se o quarto pertence a você (autorização).
Authentication vs. AuthorizationBOLA (Broken Object Level Authorization)IDOR (Insecure Direct Object Reference)CWE-639) na qual objetos de implementação interna são expostos diretamente aos usuários sem verificações de controle de acesso (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