Input Sanitization and HTTP HeadersΒΆ
π― What Youβll LearnΒΆ
- Why input sanitization is critical for preventing attacks like SQL injection and XSS
- How FastAPI and Pydantic help validate and sanitize input automatically
- How to configure secure HTTP headers to protect against common exploits
- Best practices for combining validation and headers to harden your API
π§ Step 1: Input SanitizationΒΆ
π Why sanitize input?ΒΆ
User input is the most common attack vector. Without sanitization, attackers can inject malicious code into your API. Examples:
- SQL Injection:
"' OR 1=1 --"could trick a query into returning all rows. - Cross-Site Scripting (XSS):
<script>alert('hacked')</script>could run in a browser if returned unsafely.
β How FastAPI helpsΒΆ
FastAPI uses Pydantic models to validate and parse input. This means:
- Types are enforced (
int,str,bool) - Invalid data is rejected automatically
- You can add constraints (length, regex, ranges)
π Example: Validating a signup form
from pydantic import BaseModel, EmailStr, constr
class UserCreate(BaseModel):
username: constr(min_length=3, max_length=20, regex="^[a-zA-Z0-9_]+$")
email: EmailStr
password: constr(min_length=8)
usernamemust be alphanumeric and between 3β20 charactersemailmust be a valid email formatpasswordmust be at least 8 characters
FastAPI rejects invalid input before it reaches your database.
π§ Step 2: Escaping and Cleaning InputΒΆ
Even with validation, sometimes you need to escape or clean input before storing or displaying it.
Example: Preventing XSS when rendering user content in HTML templates:
import html
def sanitize_text(text: str) -> str:
return html.escape(text)
This converts <script> into <script>, preventing execution.
π Step 3: Secure HTTP HeadersΒΆ
π Why headers matter?ΒΆ
HTTP headers instruct browsers how to handle your API responses. Misconfigured headers can expose users to attacks.
β Important Security HeadersΒΆ
- Content-Security-Policy (CSP): Restricts what scripts/styles can run
- X-Content-Type-Options: Prevents MIME type sniffing
- Strict-Transport-Security (HSTS): Forces HTTPS connections
- X-Frame-Options: Prevents clickjacking by disallowing embedding in iframes
π Example: Adding headers in FastAPI
from fastapi import FastAPI, Response
app = FastAPI()
@app.middleware("http")
async def add_security_headers(request, call_next):
response: Response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
response.headers["Content-Security-Policy"] = "default-src 'self'"
return response
Refer to the following links for more details on security headers:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options
π§ Step 4: Best PracticesΒΆ
- Always validate input with Pydantic models
- Escape user-generated content before rendering in HTML
- Apply strict security headers globally with middleware
- Combine with HTTPS and rate limiting for layered defense
π§ͺ Practice ChallengeΒΆ
- Create a
CommentCreatemodel that:- Requires
textto be 1β200 characters - Rejects empty or overly long comments
- Requires
- Add middleware to enforce:
X-Frame-Options: SAMEORIGINContent-Security-Policy: default-src 'self'
- Test by sending malicious input like
<script>alert('xss')</script>and confirm itβs safely escaped.
π§ RecapΒΆ
You now know how to:
- Sanitize and validate input to block SQL injection and XSS
- Use Pydantic constraints for strong validation
- Apply secure HTTP headers to harden your API responses
Together, these practices make your FastAPI app resilient against common web attacks.