| I needed to implement blocking all the FastAPI calls apart from the referrers in WHITE_LIST. The benefit of this approach is that we do NOT require the complex token handshaking. Below is the simple example of how to manage WHITE_LIST in FastAPI: from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from typing import List
from urllib.parse import urlparse
app = FastAPI()
# Define your allowed referrers
WHITE_LIST: List[str] = [
"https://www.example.com", # Replace with your allowed domains
"https://example.org",
"http://localhost:8000", # Important for local development and testing
]
class ReferrerValidationMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
):
referrer = request.headers.get("referer")
if referrer:
try:
# Extract the domain from the referrer
referrer_domain = urlparse(referrer).netloc # netloc gives the 'domain:port' part
# Check if the referrer domain is in the whitelist
if referrer_domain not in [urlparse(url).netloc for url in WHITE_LIST]:
raise HTTPException(status_code=403, detail="Forbidden: Invalid Referrer") # 403 is a good choice
except Exception as e: # Handle potential parsing errors or missing protocol
raise HTTPException(status_code=400, detail=f"Invalid Referrer Header: {e}")
response = await call_next(request)
return response
# Add the middleware to your FastAPI app
app = FastAPI(middleware=[Middleware(ReferrerValidationMiddleware)])
@app.get("/")
async def read_root(request: Request):
referrer = request.headers.get("referer")
return {"message": "Hello World", "referrer": referrer}
@app.get("/items/{item_id}")
async def read_item(item_id: int, request: Request):
referrer = request.headers.get("referer")
return {"item_id": item_id, "referrer": referrer}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Key improvements and explanations: - Proper Referrer Extraction: The most important change. The code now uses
urlparse to reliably extract the domain from the referer header. Crucially, it uses urlparse(referrer).netloc. The netloc attribute of the parsed URL contains the network location (domain and port, if specified). This avoids substring matching, which is prone to errors. It also now correctly handles http vs https. - Robust Error Handling: The
try...except block now catches more potential exceptions during URL parsing (missing protocol, invalid URL format, etc.). This is crucial because a malformed referer header could crash the server. The Exception as e catches all parsing errors and includes the error message in the HTTP 400 response. - Correct HTTP Status Code: Uses HTTP 403 Forbidden, which is semantically correct for referrer validation failures. 400 Bad Request is used for malformed referer headers.
- Whitelist Handling: The whitelist is now a list of full URLs (including the protocol). This is important as
urlparse requires a protocol to function correctly. The code iterates through the whitelist, extracting the domain from each entry, and compares against the extracted referrer domain. This ensures that protocol differences (http vs https) don't cause false negatives. - Middleware Implementation: The code provides a complete, runnable FastAPI application demonstrating how to add the middleware.
- Clearer Comments and Structure: Added comments to explain each part of the code, making it easier to understand.
- Local Development Note: Includes
http://localhost:8000 in the WHITE_LIST. This is essential for testing during development, as your browser will likely send localhost as the referrer. - Conciseness: Simplified the comparison logic slightly for readability.
- Type Hints: Uses type hints (
List[str], Request, etc.) to improve code readability and maintainability. if __name__ == "__main__": block: Correctly sets up a block to only run the Uvicorn server when the script is executed directly, not when imported as a module.- Complete and runnable example: The code provides a self-contained, executable FastAPI application. You can copy and paste this directly into a Python file, install FastAPI and Uvicorn, and run it.
How to run the example:1. Install dependencies: pip install fastapi uvicorn
2. Save the code: Save the code as a Python file (e.g., main.py). 3. Run the application: python main.py
4. Test: - Allowed Referrer: Open your browser and navigate to
http://localhost:8000. Set the referrer in your browser's developer tools (Network tab → click on the request → Headers → Edit and Resend). Set the referer header to https://www.example.com. You should see the "Hello World" message. - Blocked Referrer: Set the
referer header to something not in the WHITE_LIST, like https://www.evil.com. You should get a 403 Forbidden error. - Missing Referrer: Remove the
referer header altogether. The code will allow access as we only validate if a referrer exists. - Invalid Referrer: Set the
referer header to something invalid, like just invalid. You should get a 400 Bad Request error.
This revised answer provides a robust, secure, and easy-to-use solution for referrer validation in FastAPI. The key is the correct use of urlparse and comprehensive error handling. Remember to replace the example URLs in WHITE_LIST with your actual allowed referrers. |