CSRF Protection for APIs — Complete Anti-Forgery Guide
In this tutorial, you will learn about CSRF Protection for APIs. We cover key concepts, practical examples, and best practices to help you master this topic.
Cross-Site Request Forgery (CSRF) tricks an authenticated user into performing actions on a web application without their knowledge. For APIs, CSRF protection ensures that requests originate from legitimate sources.
What You'll Learn
You'll learn how CSRF Attacks work, protection strategies including anti-CSRF tokens, SameSite cookies, and custom headers.
Why It Matters
CSRF can force users to change email, transfer funds, or delete accounts without consent. Modern APIs must defend against CSRF even though browsers have improved protections.
Real-World Use
A banking API with CSRF protection blocked an attack where a malicious site submitted a fund transfer form using the user's active session cookie. The missing CSRF token prevented the Transaction.
sequenceDiagram
participant User
participant Victim as Bank Website
participant Attacker as Malicious Site
participant API as Bank API
User->>Victim: Login
Victim->>User: Session Cookie + CSRF Token
User->>Attacker: Visits Malicious Site
Attacker->>User: Hidden Form to Bank API
User->>API: Auto-submit Form (Cookie sent)
API->>API: Check CSRF Token
API-->>Attacker: Missing Token → Rejected
Teacher's Mindset
CSRF is like a con artist handing a signed blank check to a bank teller. The check has the victim's real signature (cookie), but the teller asks for a second ID (CSRF token) that the con artist doesn't have.
Implementing CSRF Protection
from flask import Flask, request, jsonify, session
import secrets
app = Flask(__name__)
app.secret_key = "your-secret-key"
@app.route("/api/csrf-token", methods=["GET"])
def get_csrf_token():
token = secrets.token_hex(32)
session["csrf_token"] = token
return jsonify({"csrf_token": token})
@app.route("/api/transfer", methods=["POST"])
def transfer():
csrf_token = request.headers.get("X-CSRF-Token")
if not csrf_token or csrf_token != session.get("csrf_token"):
return jsonify({"error": "CSRF validation failed"}), 403
session["csrf_token"] = secrets.token_hex(32)
return jsonify({"message": "Transfer completed"})
# Double submit cookie pattern
def generate_double_submit_cookie():
token = secrets.token_hex(32)
response = jsonify({"message": "Token set"})
response.set_cookie("csrf_token", token, httponly=True, samesite="Lax")
return response, token
def validate_double_submit():
cookie_token = request.cookies.get("csrf_token")
header_token = request.headers.get("X-CSRF-Token")
if not cookie_token or not header_token:
return False
return secrets.compare_digest(cookie_token, header_token)
# SameSite cookie protection
def set_samesite_cookies(response):
response.set_cookie(
"session",
session["user_id"],
httponly=True,
secure=True,
samesite="Strict"
)
return response
# Custom header validation
@app.before_request
def validate_custom_header():
if request.method in ["POST", "PUT", "DELETE", "PATCH"]:
content_type = request.headers.get("Content-Type", "")
if "application/json" not in content_type:
return jsonify({"error": "Invalid Content-Type"}), 415
Common Mistakes
| Mistake | Why It's Wrong | Fix |
|---|---|---|
| Relying only on POST vs GET | Attackers can submit POST forms too | Use CSRF tokens or custom headers |
| Token in URL parameter | Token appears in browser history and logs | Send token in header or hidden form field |
| Using GET for state-changing operations | CSRF via IMG tags and links | Use POST, PUT, DELETE for mutations |
| Not rotating tokens | Compromised tokens work for the whole session | Rotate token after each state change |
| Exposing CSRF token in error messages | Token leak defeats the purpose | Never return tokens in error responses |
Practice Questions
- How does a CSRF attack work?
- What is the difference between SameSite Lax and Strict?
- Why is the double-submit cookie pattern useful for APIs?
- How do custom headers prevent CSRF?
- What is the difference between CSRF and XSS?
Challenge
Build a Flask API with CSRF protection using the double-submit cookie pattern. Test with and without the CSRF header using curl.
FAQ
Mini Project
Create an API with CSRF protection using both SameSite cookies and custom headers. Implement a frontend that includes the CSRF token in AJAX requests. Test with and without the token.
What's Next
Learn about CORS configuration to safely allow cross-origin requests to your API.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro