flawopen.com/bola-idor/Python

● CWE-639 · 严重
安全研究 · FlawOpen

Python 中的对象级权限失效 (BOLA / IDOR) 防护

彻底解决 Python 与 FastAPI 中的 BOLA (CWE-639) / IDOR 越权漏洞:在 ORM 查询层强制绑定当前认证租户组织 ID,拒绝盲目信任前端传递的对象主键。

💡 通俗易懂的原理解析 (ELI5)

就像住酒店拿到 204 房卡,发现把房门号改成 205 就能开门偷看别人的行李。酒店验证了你是注册住客(身份认证),却没有验证你是否有权进入 205 号房间(对象授权)。BOLA 就是攻击者修改 API 中的 ID 查看他人隐私。

核心概念与专有名词

Authentication vs. Authorization
技术概念 (Authentication vs. Authorization):Authentication confirms WHO the user is (valid login). Authorization confirms WHAT resources that specific user is permitted to view or modify.
BOLA (Broken Object Level Authorization)
技术概念 (BOLA (Broken Object Level Authorization)):The #1 OWASP API vulnerability where an endpoint exposes an object reference (e.g. /api/invoices/1042) without verifying tenant ownership.
IDOR (Insecure Direct Object Reference)
技术概念 (IDOR (Insecure Direct Object Reference)):The broader vulnerability class (CWE-639) where internal implementation objects are exposed directly to users without access control checks.
Tenancy Scope Injection
技术概念 (Tenancy Scope Injection):An architectural pattern where database queries automatically enforce WHERE organization_id = :auth_user_org_id at the ORM layer.
Non-Enumerable Identifiers (UUIDv4)
技术概念 (Non-Enumerable Identifiers (UUIDv4)):Using cryptographically random 128-bit identifiers instead of sequential auto-incrementing integers (1, 2, 3...) to prevent predictable enumeration.

攻击执行流程分解

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.

源代码对比:漏洞与安全实现

✕ 存在漏洞的实现
# 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

工程与系统安全加固清单

References