Origin validation errors

Apply appropriate origin validation and CSRF protection to requests whose credentials are sent automatically by the browser.

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 Origin headers and null origins.
  • 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 active CsrfViewMiddleware; 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.CSRFProtect to 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 Origin headers and values outside the allow-list. TrustedHostMiddleware separately checks the destination Host; it does not replace CSRF protection.

References