GuardAPI

Fix BOLA (Broken Object Level Authorization) in FastAPI

Generated remediation sketch. GuardAPI the product only gates GET BOLA in CI.

BOLA (Broken Object Level Authorization) is the crown jewel of API exploitation. It occurs when an endpoint exposes a resource via an identifier (like /api/orders/123) but fails to verify if the authenticated user actually owns that resource. In FastAPI, this usually stems from blindly trusting path parameters and failing to enforce ownership checks at the database query level.

The Vulnerable Pattern

@app.get("/invoice/{invoice_id}")
async def get_invoice(invoice_id: int, db: Session = Depends(get_db)):
    # VULNERABLE: Only checks if the invoice exists, not who it belongs to.
    invoice = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not invoice:
        raise HTTPException(status_code=404)
    return invoice

The Secure Implementation

The fix moves authorization from a 'check' to a 'constraint'. By injecting the authenticated `current_user` and including their unique ID in the database filter, we ensure the application logic is physically incapable of retrieving another user's data. Hardening tip: Use UUIDs instead of sequential integers for IDs to make brute-forcing harder, but never rely on 'security by obscurity'—always enforce the ownership check in the query.

@app.get("/invoice/{invoice_id}")
async def get_invoice(
    invoice_id: int, 
    current_user: User = Depends(get_current_active_user), 
    db: Session = Depends(get_db)
):
    # SECURE: Query includes the owner_id constraint to ensure authorization.
    invoice = db.query(Invoice).filter(
        Invoice.id == invoice_id, 
        Invoice.owner_id == current_user.id
    ).first()
if not invoice:
    # Return 404 instead of 403 to prevent ID enumeration
    raise HTTPException(status_code=404, detail="Invoice not found")
return invoice</code></pre>

Prove it on the next pull request

This page is a generated code sketch, not a GuardAPI scan. After you scope the query by tenant, fail the GitHub job when tenant B can still GET tenant A's object. GET-only. Tokens stay in GitHub Secrets.

- uses: GuardAPI/ghost-api@v6
  with:
    api-key: ${{ secrets.GUARD_API_KEY }}
    openapi-path: ./openapi.json
    base-url: ${{ secrets.STAGING_API_URL }}
    token-a: ${{ secrets.TOKEN_USER_A }}
    token-b: ${{ secrets.TOKEN_USER_B }}

Install docs · 14-day trial · How the fail rule works

About this page

Framework notes in /guides are generated sketches kept for URL stability. They are not human pentest reports and they are not GuardAPI scan output. The product is a GET-only BOLA merge gate. Maintained by GuardAPI. Questions: support@guard-api.com