Input Sanitization and Resource Protection

Input Sanitization and Resource Protection

Trust no one. Learn how to sanitize user input and implement cross-user resource protection to prevent IDOR attacks and data leaks.

Input Sanitization and Resource Protection

In the previous lesson, we secured our "Gates" with passwords and secrets. In this lesson, we look at the "Rooms" inside. Even after a user is logged in, you must ensure they can only see their Own Data.

This is the home of the IDOR (Insecure Direct Object Reference) attack—one of the most common vulnerabilities in APIs.


1. What is an IDOR Attack?

Imagine an endpoint: GET /orders/123. If User A (who owns order 123) logs in, they see their order. But if User A changes the URL to GET /orders/789 (which belongs to User B), does your API show it to them?

If it does, you have an IDOR vulnerability.

The Fix: Check Ownership

You must always verify that the resource being requested belongs to the current_user.

@app.get("/orders/{order_id}")
async def get_order(order_id: int, current_user: User = Depends(get_current_user)):
    order = db.fetch_order(order_id)
    
    # CRITICAL SECURITY CHECK
    if order.user_id != current_user.id:
        raise HTTPException(status_code=403, detail="Not authorized to view this order")
    
    return order

2. Input Sanitization (The Pydantic Shield)

Pydantic handles 90% of sanitization by enforcing types. If you expect an int, and the user sends a <script>alert(1)</script>, Pydantic will reject it before it ever reaches your business logic.

However, for Strings, you might want to strip HTML or extra whitespace.

from pydantic import field_validator

class Comment(BaseModel):
    text: str

    @field_validator('text')
    def clean_text(cls, v):
        # Basic example: Strip whitespace and potentially HTML tags
        return v.strip().replace("<", "&lt;").replace(">", "&gt;")

3. Protecting against Mass Assignment

Mass Assignment happens when a user sends extra fields in a JSON request that you didn't expect, and your code accidentally saves them.

Example: A user sends {"username": "hacker", "is_admin": true} to your /update-profile endpoint. If your code just does user.update(data), they just made themselves an admin!

The Solution: Always use strict Pydantic models for updates that only include safe fields.


4. HTTPS and Secure Cookies

In production, you must use HTTPS (encryption in transit).

  • FastAPI automatically supports secure headers.
  • If using cookies, always set httponly=True and secure=True.

Visualizing Resource Protection

graph TD
    A["User A (ID:1)"] --> B["GET /document/55"]
    B --> C["Fetch Document 55 from DB"]
    C --> D{"Does owner_id == 1?"}
    D -- "No" --> E["Return 403 Forbidden"]
    D -- "Yes" --> F["Return Document"]
    
    style E fill:#f66,stroke:#333

Summary

  • IDOR: Always check that the requester owns the resource.
  • Sanitization: Use Pydantic to filter and clean strings.
  • Mass Assignment: Use dedicated "Update" models to prevent users from modifying system fields.
  • Defense in Depth: Security happens at every layer—from the URL to the Database query.

In the next lesson, we wrap up Module 16 with Exercises on securing your FastAPI application.


Exercise: The Resource Guard

Design a model for a PrivateNote.

  1. It has an id, content, and owner_id.
  2. Write a FastAPI endpoint PATCH /notes/{note_id} that updates the content but only if the requester is the owner.

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Subscribe on LinkedIn