Description
When browsers automatically send credentials such as cookies, inadequate origin validation and CSRF protection may let an attacker use another site to send unwanted requests with the user's permissions. Checking Origin does not replace user authentication: clients outside the browser can set this header themselves.
Potential impact
- CSRF: An attacker may send malicious requests on the user's behalf to attempt unauthorized access.
- Data exposure: Requests that change disclosure or forwarding settings may expose sensitive data.
- Data modification: An attacker may change data and compromise system integrity.
Remediation
- Compare the origin's scheme, host and port against an exact allow-list on the server. Define how to handle missing
Originheaders andnullorigins. - Apply the framework's CSRF token validation to state-changing requests that use browser cookie authentication.
- Configure CORS so only trusted origins can read responses. CORS alone does not replace CSRF protection, authentication or authorization.
Examples
These are request-handling excerpts for cookie-based authentication. Apply authentication and authorization separately. Supply a sufficiently random secret through Flask's your_secret_key environment variable before startup; clients must send a valid CSRF token.
Django
Before
python
# Unsafe Django code
from django.http import HttpResponse
def my_view(request):
if request.method == 'POST':
# Process the request without CSRF validation
return HttpResponse("Request processed")
After
python
# Safe Django code
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def my_view(request):
if request.method == 'POST':
# Process the request after validating the CSRF token
return HttpResponse("Request processed")
Explanation:
- Before: This excerpt shows no CSRF protection. A cookie-authenticated request may be vulnerable if no other layer protects it.
- After: Applies CSRF validation with
@csrf_protect. The earlier version may already be protected by an activeCsrfViewMiddleware; absence of the decorator alone is not conclusive.
Flask
Before
python
# Unsafe Flask code
from flask import Flask, request
app = Flask(__name__)
@app.route('/submit', methods=['POST'])
def submit():
# Process the request without CSRF validation
return "Request processed"
After
python
# Safe Flask code
import os
from flask import Flask, request
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv("your_secret_key").encode()
csrf = CSRFProtect(app)
@app.route('/submit', methods=['POST'])
def submit():
return "Request processed"
Explanation:
- Before: This excerpt shows no CSRF protection. A cookie-authenticated request may be vulnerable if no other layer protects it.
- After: Uses
flask_wtf.csrf.CSRFProtectto validate the CSRF token.
FastAPI
Before
python
# Unsafe FastAPI code
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/submit")
async def submit(request: Request):
# Process the request without CSRF validation
return {"message": "Request processed"}
After
python
# Safe FastAPI code
from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
# Configure trusted hosts
app.add_middleware(
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
)
@app.post("/submit")
async def submit(request: Request):
origin = request.headers.get('origin')
if origin not in ["https://example.com", "https://www.example.com"]:
raise HTTPException(status_code=403, detail="Invalid origin")
# Process the request
return {"message": "Request processed"}
Explanation:
- Before: This excerpt shows no CSRF protection. A cookie-authenticated request may be vulnerable if no other layer protects it.
- After: Rejects missing
Originheaders and values outside the allow-list.TrustedHostMiddlewareseparately checks the destinationHost; it does not replace CSRF protection.