Cross-site request forgery

Cross-site request forgery (CSRF)

Description

Cross-site request forgery (CSRF) occurs when a malicious website causes a user's browser to send an unwanted request to another site. It abuses credentials that the browser sends automatically, such as cookies, to perform actions the attacker chooses, including modifying or deleting data.

Potential impact

  • Data modification: Data may be changed without the user's intent.
  • Misuse of user permissions: Account changes or administrative actions available to the victim may be performed.
  • Service abuse: An attacker may misuse application features to cause harm.

Remediation

  • Use CSRF tokens for state-changing requests.
  • Validate request origins on the server, using Origin and related checks to reject untrusted sources.
  • Do not change state through GET requests. Use HTTPS and appropriate SameSite cookie settings alongside CSRF protection.

Examples

Django

The first example assumes there is no CsrfViewMiddleware or other CSRF protection. The absence of a decorator alone does not establish that a view is unprotected.

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: Processes POST requests without CSRF validation under the stated configuration.
  • After: Uses @csrf_protect to validate CSRF tokens.

Flask

The Flask example assumes a sufficiently strong secret key is set in the your_secret_key environment variable. Protected forms or AJAX requests must include the CSRF token.

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():
    # Process the request after validating the CSRF token
    return "Request processed"

Explanation:

  • Before: Processes POST requests without CSRF validation.
  • After: Uses flask_wtf.csrf.CSRFProtect to validate CSRF tokens.

FastAPI

These examples assume a state-changing endpoint with cookie-based authentication. The second example validates Origin and rejects requests when it is absent or outside the allow-list. Authentication and authorization still need separate implementation.

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: Processes POST requests without CSRF validation.
  • After: The Origin check rejects requests from unapproved origins. TrustedHostMiddleware checks the destination Host header; it does not prevent CSRF on its own.

References