Bola Idor in Fastapi
How Bola Idor Manifests in Fastapi
BOLA/IdOR (Broken Object Level Authorization / Insecure Direct Object Reference) in FastAPI applications typically occurs when route handlers accept user-supplied identifiers without validating that the authenticated user has permission to access the referenced resource. FastAPI's dependency injection system and Pydantic models can both help and hinder security depending on implementation.
The most common pattern in FastAPI is using path parameters like /users/{user_id} or query parameters for resource identification. When developers retrieve objects directly using these parameters without ownership checks, attackers can simply modify the ID to access other users' data.
@app.get('/api/users/{user_id}')
def get_user(user_id: int):
# SECURITY ISSUE: No authorization check
user = User.get(id=user_id)
return user.dict()This pattern is particularly dangerous in FastAPI because the type hints and automatic validation can create a false sense of security. The framework validates that user_id is an integer, but this doesn't verify authorization.
FastAPI's dependency injection can introduce BOLA/IdOR when dependencies return objects without proper scoping. Consider this common anti-pattern:
@app.get('/api/posts/{post_id}')
async def get_post(post: Post = Depends(get_post_by_id)):
# SECURITY ISSUE: get_post_by_id doesn't check ownership
return post.dict()The get_post_by_id dependency might look like:
def get_post_by_id(post_id: int = Path(...)):
return Post.get(id=post_id)Without ownership validation, any authenticated user can access any post by changing the ID.
Database query patterns in FastAPI often lead to BOLA/IdOR. ORMs like SQLAlchemy or Tortoise ORM make it easy to write insecure queries:
@app.get('/api/orders/{order_id}')
async def get_order(order_id: int):
order = await Order.filter(id=order_id).first()
return order.dict() # SECURITY: No user validationFastAPI's background tasks and async endpoints can compound the problem. Developers might forget authorization checks in async functions:
@app.get('/api/documents/{doc_id}', response_model=Document)
async def get_document(doc_id: str):
doc = await fetch_document_async(doc_id)
return doc # SECURITY: Missing user ownership checkPydantic models in FastAPI can also introduce BOLA/IdOR through improper field exposure. If a model includes sensitive fields and isn't properly scoped:
class UserOut(BaseModel):
id: int
email: str
ssn: str # SECURITY: Exposing sensitive data
address: strFastAPI's automatic OpenAPI generation can inadvertently document endpoints that have authorization bypasses, making it easier for attackers to discover vulnerable endpoints.
Fastapi-Specific Detection
Detecting BOLA/IdOR in FastAPI applications requires examining both the code structure and runtime behavior. Static analysis can identify common patterns, but dynamic scanning with middleBrick provides comprehensive coverage.
middleBrick's black-box scanning approach is particularly effective for FastAPI applications because it tests the actual running API without requiring source code access. The scanner sends authenticated requests with modified identifiers to detect authorization bypasses.
For FastAPI specifically, middleBrick examines route patterns and tests parameter manipulation across all endpoints. The scanner identifies FastAPI's path parameter conventions and systematically tests for BOLA/IdOR by:
- Modifying numeric IDs in GET, PUT, PATCH, DELETE requests
- Testing UUID and string-based identifiers
- Checking nested resource endpoints like
/users/{user_id}/posts/{post_id} - Analyzing response differences to detect information disclosure
middleBrick's OpenAPI analysis is particularly valuable for FastAPI since the framework auto-generates comprehensive specs. The scanner cross-references the spec with runtime findings to identify endpoints that accept identifiers without proper authorization.
FastAPI's dependency injection system can be analyzed statically for BOLA/IdOR patterns. Look for dependencies that:
# Vulnerable pattern - no user validation
async def get_user_by_id(user_id: int = Depends()) -> User:
return await User.filter(id=user_id).first()middleBrick's LLM/AI security scanning also detects BOLA/IdOR in AI endpoints, which is increasingly important as FastAPI powers many AI/ML APIs. The scanner tests for unauthorized access to AI models, training data, or user-specific AI configurations.
Runtime detection involves monitoring API logs for suspicious patterns like:
- Rapid sequential ID requests (enumeration attempts)
- Requests with IDs from different users in short timeframes
- 404 responses that should return 403 (information leakage)
middleBrick's continuous monitoring in the Pro plan automatically scans FastAPI APIs on schedules, alerting when new endpoints appear or when authorization patterns change. This is crucial for FastAPI applications that frequently evolve with new endpoints.
The scanner's 12 security checks include specific BOLA/IdOR detection that understands FastAPI's conventions, making it more effective than generic API scanners. It tests authenticated and unauthenticated access patterns to identify privilege escalation opportunities.
Fastapi-Specific Remediation
Remediating BOLA/IdOR in FastAPI requires implementing proper authorization checks at the appropriate layers. The most effective approach combines FastAPI's dependency injection with ownership validation.
First, create a dependency that validates resource ownership:
async def get_user_owned_post(
post_id: int = Path(...),
current_user: User = Depends(get_current_active_user)
) -> Post:
post = await Post.filter(id=post_id).first()
if not post:
raise HTTPException(status_code=404, detail='Post not found')
if post.user_id != current_user.id:
raise HTTPException(status_code=403, detail='Not authorized')
return postUse this dependency in your route handlers:
@app.get('/api/posts/{post_id}')
async def get_post(post: Post = Depends(get_user_owned_post)):
return post.dict()For FastAPI applications with complex authorization rules, implement a permission system:
class Permissions:
@staticmethod
async def check_ownership(resource, user):
if resource.user_id != user.id:
raise HTTPException(403, 'Not authorized')
@staticmethod
async def check_admin(user):
if not user.is_admin:
raise HTTPException(403, 'Admin access required')FastAPI's middleware can enforce authorization at a broader level:
class AuthorizationMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope['type'] == 'http' and scope['method'] in ['GET', 'POST', 'PUT', 'DELETE']:
# Check authorization headers, tokens, etc.
pass
return await self.app(scope, receive, send)For FastAPI applications using SQLAlchemy, implement query filters that automatically scope to the current user:
async def get_user_posts(
current_user: User = Depends(get_current_active_user),
skip: int = 0,
limit: int = 100
):
posts = await Post.filter(
user_id=current_user.id
).offset(skip).limit(limit).all()
return postsFastAPI's response_model parameter helps prevent data exposure:
class PostOut(BaseModel):
id: int
title: str
content: str
# Excludes sensitive fields like user_id, created_at
@router.get('/posts/{post_id}', response_model=PostOut)
async def get_post(post: Post = Depends(get_user_owned_post)):
return postFor batch operations in FastAPI, validate all items in a request:
@app.put('/api/posts/batch')
async def update_posts(posts_update: List[PostUpdate] = Body(...)):
current_user = get_current_active_user()
for update in posts_update:
post = await Post.filter(id=update.id).first()
if post.user_id != current_user.id:
raise HTTPException(403, 'Not authorized to update this post')
# Update logic
return {'status': 'success'}FastAPI's exception handlers can provide consistent error responses for authorization failures:
@app.exception_handler(Forbidden):
async def forbidden_exception_handler(request, exc):
return JSONResponse(
status_code=403,
content={'detail': 'Insufficient permissions to access this resource'}
)Using middleBrick's CLI tool during development helps catch BOLA/IdOR issues early:
middlebrick scan https://api.example.com --auth-token $TOKEN --output jsonThe GitHub Action integration ensures BOLA/IdOR checks are part of your CI/CD pipeline:
- name: Run middleBrick Security Scan
uses: middlebrick/middlebrick-action@v1
with:
api_url: ${{ secrets.API_URL }}
auth_token: ${{ secrets.API_TOKEN }}
fail_below_score: CRelated CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |