HIGH bola idorfastapi

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 validation

FastAPI'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 check

Pydantic 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: str

FastAPI'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 post

Use 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 posts

FastAPI'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 post

For 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 json

The 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: C

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

How does middleBrick detect BOLA/IdOR in FastAPI applications?
middleBrick uses black-box scanning to test FastAPI endpoints by sending authenticated requests with modified identifiers. It systematically changes IDs in path parameters and query parameters, checking if users can access resources they don't own. The scanner analyzes FastAPI's OpenAPI specs to understand endpoint structures and tests both authenticated and unauthenticated access patterns. It identifies response differences that indicate information disclosure and tests nested resource endpoints common in FastAPI applications.
Can middleBrick scan FastAPI applications without source code access?
Yes, middleBrick performs black-box scanning that requires only the API URL. It sends HTTP requests to your running FastAPI application, tests authentication mechanisms, and analyzes responses to identify security vulnerabilities. This approach works perfectly for FastAPI's auto-generated OpenAPI specs and doesn't require any modifications to your application code or access to your source repository.