
Csrf Protection
- 427 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
csrf-protection is an agent skill that helps developers design and verify CSRF defenses using synchronizer tokens, double-submit cookies, SameSite attributes, and Origin validation for state-changing web requests.
About
csrf-protection is an aj-geddes/useful-ai-prompts skill for securing cookie-authenticated forms and state-changing HTTP operations against cross-site request forgery. The quick-start demonstrates a Node.js CSRFProtection class generating crypto.randomBytes(32) hex tokens with one-hour expiry, paired with guidance on csurf middleware integration. Five reference guides cover Node.js and Express CSRF protection, double-submit cookie pattern, Python Flask CSRF protection, frontend token wiring, and Origin or Referer header validation. Best practices require CSRF tokens on all POST, PUT, and DELETE operations, SameSite=Strict cookies, HTTPS-only transport, secure random tokens with expiration, and explicit AJAX header inclusion while warning against skipping protection for authenticated routes or storing tokens in localStorage. Developers reach for csrf-protection when building login forms, account settings, payment actions, or reviewing SPA fetch calls that mutate server state with session cookies.
- Token generation and validation patterns
- SameSite and cookie attribute guidance
- Safe POST, PUT, and DELETE endpoint checks
- Double-submit and synchronizer token options
- Framework-specific CSRF middleware setup
Csrf Protection by the numbers
- 427 all-time installs (skills.sh)
- Ranked #538 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill csrf-protectionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 427 |
|---|---|
| repo stars | ★ 305 |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you secure forms against CSRF attacks?
Design and verify CSRF defenses for cookie-authenticated forms and APIs, including tokens, SameSite settings, and safe state-changing endpoint patterns.
Who is it for?
Web engineers shipping cookie-session authentication who must protect forms, account actions, and payment endpoints from forged cross-site requests.
Skip if: Pure Bearer-token APIs with no browser cookies or form posts, where CSRF token infrastructure adds no meaningful threat reduction.
When should I use this skill?
A developer implements login or payment forms, reviews POST endpoints with session cookies, or asks about CSRF tokens, SameSite, or double-submit cookies.
What you get
CSRF token middleware, double-submit cookie setup, SameSite cookie config, frontend token headers, and Origin validation checklist.
- CSRF middleware configuration
- Frontend token header wiring
- Origin validation checklist
By the numbers
- Quick-start generates 32-byte CSRF tokens with 1-hour expiry
- Includes 5 reference guides in the references directory
Files
CSRF Protection
Table of Contents
Overview
Implement comprehensive Cross-Site Request Forgery protection using synchronizer tokens, double-submit cookies, SameSite cookie attributes, and custom headers.
When to Use
- Form submissions
- State-changing operations
- Authentication systems
- Payment processing
- Account management
- Any POST/PUT/DELETE requests
Quick Start
Minimal working example:
// csrf-protection.js
const crypto = require("crypto");
const csrf = require("csurf");
class CSRFProtection {
constructor() {
this.tokens = new Map();
this.tokenExpiry = 3600000; // 1 hour
}
/**
* Generate CSRF token
*/
generateToken() {
return crypto.randomBytes(32).toString("hex");
}
/**
* Create token for session
*/
createToken(sessionId) {
const token = this.generateToken();
const expiry = Date.now() + this.tokenExpiry;
this.tokens.set(sessionId, {
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| Node.js/Express CSRF Protection | Node.js/Express CSRF Protection |
| Double Submit Cookie Pattern | Double Submit Cookie Pattern |
| Python Flask CSRF Protection | Python Flask CSRF Protection |
| Frontend CSRF Implementation | Frontend CSRF Implementation |
| Origin and Referer Validation | Origin and Referer Validation |
Best Practices
✅ DO
- Use CSRF tokens for all state-changing operations
- Set SameSite=Strict on cookies
- Validate Origin/Referer headers
- Use secure, random tokens
- Implement token expiration
- Use HTTPS only
- Include tokens in AJAX requests
- Test CSRF protection
❌ DON'T
- Skip CSRF for authenticated requests
- Use GET for state changes
- Trust Origin header alone
- Reuse tokens
- Store tokens in localStorage
- Allow credentials in CORS without validation
Double Submit Cookie Pattern
Double Submit Cookie Pattern
// double-submit-csrf.js
const crypto = require("crypto");
class DoubleSubmitCSRF {
/**
* Generate CSRF token and set cookie
*/
static generateAndSetToken(res) {
const token = crypto.randomBytes(32).toString("hex");
// Set CSRF cookie
res.cookie("XSRF-TOKEN", token, {
httpOnly: false, // Allow JS to read for double submit
secure: true,
sameSite: "strict",
maxAge: 3600000,
});
return token;
}
/**
* Middleware to validate double submit
*/
static middleware() {
return (req, res, next) => {
// Skip GET, HEAD, OPTIONS
if (["GET", "HEAD", "OPTIONS"].includes(req.method)) {
return next();
}
const cookieToken = req.cookies["XSRF-TOKEN"];
const headerToken = req.headers["x-xsrf-token"];
if (!cookieToken || !headerToken) {
return res.status(403).json({
error: "csrf_token_missing",
});
}
// Compare tokens (timing-safe)
if (
!crypto.timingSafeEqual(
Buffer.from(cookieToken),
Buffer.from(headerToken),
)
) {
return res.status(403).json({
error: "csrf_token_mismatch",
});
}
next();
};
}
}
// Express setup
const app = express();
const cookieParser = require("cookie-parser");
app.use(cookieParser());
app.use(express.json());
// Generate token on login
app.post("/api/login", async (req, res) => {
// Authenticate user
const token = DoubleSubmitCSRF.generateAndSetToken(res);
res.json({
message: "Login successful",
csrfToken: token,
});
});
// Protected routes
app.use("/api/*", DoubleSubmitCSRF.middleware());
app.post("/api/update-profile", (req, res) => {
// Update profile
res.json({ message: "Profile updated" });
});Frontend CSRF Implementation
Frontend CSRF Implementation
// csrf-client.js
class CSRFClient {
constructor() {
this.token = null;
this.tokenExpiry = null;
}
/**
* Fetch CSRF token from server
*/
async fetchToken() {
const response = await fetch("/api/csrf-token", {
credentials: "include",
});
const data = await response.json();
this.token = data.csrfToken;
this.tokenExpiry = Date.now() + 3600000; // 1 hour
return this.token;
}
/**
* Get valid token (fetch if needed)
*/
async getToken() {
if (!this.token || Date.now() > this.tokenExpiry) {
await this.fetchToken();
}
return this.token;
}
/**
* Make protected request
*/
async request(url, options = {}) {
const token = await this.getToken();
const headers = {
"Content-Type": "application/json",
"X-CSRF-Token": token,
...options.headers,
};
return fetch(url, {
...options,
headers,
credentials: "include",
});
}
/**
* POST request with CSRF token
*/
async post(url, data) {
return this.request(url, {
method: "POST",
body: JSON.stringify(data),
});
}
/**
* PUT request with CSRF token
*/
async put(url, data) {
return this.request(url, {
method: "PUT",
body: JSON.stringify(data),
});
}
/**
* DELETE request with CSRF token
*/
async delete(url) {
return this.request(url, {
method: "DELETE",
});
}
}
// Usage
const client = new CSRFClient();
async function transferFunds() {
try {
const response = await client.post("/api/transfer", {
amount: 1000,
toAccount: "123456",
});
const result = await response.json();
console.log("Transfer successful:", result);
} catch (error) {
console.error("Transfer failed:", error);
}
}
// React hook for CSRF
function useCSRF() {
const [token, setToken] = React.useState(null);
React.useEffect(() => {
async function fetchToken() {
const response = await fetch("/api/csrf-token");
const data = await response.json();
setToken(data.csrfToken);
}
fetchToken();
}, []);
return token;
}
// Usage in React form
function TransferForm() {
const csrfToken = useCSRF();
const handleSubmit = async (e) => {
e.preventDefault();
await fetch("/api/transfer", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken,
},
body: JSON.stringify({
amount: 1000,
toAccount: "123456",
}),
});
};
return (
<form onSubmit={handleSubmit}>
<input type="hidden" name="_csrf" value={csrfToken} />
{/* form fields */}
<button type="submit">Transfer</button>
</form>
);
}Node.js/Express CSRF Protection
Node.js/Express CSRF Protection
// csrf-protection.js
const crypto = require("crypto");
const csrf = require("csurf");
class CSRFProtection {
constructor() {
this.tokens = new Map();
this.tokenExpiry = 3600000; // 1 hour
}
/**
* Generate CSRF token
*/
generateToken() {
return crypto.randomBytes(32).toString("hex");
}
/**
* Create token for session
*/
createToken(sessionId) {
const token = this.generateToken();
const expiry = Date.now() + this.tokenExpiry;
this.tokens.set(sessionId, {
token,
expiry,
});
return token;
}
/**
* Validate CSRF token
*/
validateToken(sessionId, token) {
const stored = this.tokens.get(sessionId);
if (!stored) {
return false;
}
if (Date.now() > stored.expiry) {
this.tokens.delete(sessionId);
return false;
}
return crypto.timingSafeEqual(
Buffer.from(stored.token),
Buffer.from(token),
);
}
/**
* Express middleware
*/
middleware() {
return (req, res, next) => {
// Skip GET, HEAD, OPTIONS
if (["GET", "HEAD", "OPTIONS"].includes(req.method)) {
return next();
}
const token = req.headers["x-csrf-token"] || req.body._csrf;
const sessionId = req.session?.id;
if (!token) {
return res.status(403).json({
error: "csrf_token_missing",
message: "CSRF token is required",
});
}
if (!this.validateToken(sessionId, token)) {
return res.status(403).json({
error: "csrf_token_invalid",
message: "Invalid or expired CSRF token",
});
}
next();
};
}
}
// Express setup with csurf package
const express = require("express");
const session = require("express-session");
const cookieParser = require("cookie-parser");
const app = express();
// Session configuration
app.use(cookieParser());
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 3600000,
},
}),
);
// CSRF protection middleware
const csrfProtection = csrf({
cookie: {
httpOnly: true,
secure: true,
sameSite: "strict",
},
});
app.use(csrfProtection);
// Provide token to templates
app.use((req, res, next) => {
res.locals.csrfToken = req.csrfToken();
next();
});
// API endpoint to get CSRF token
app.get("/api/csrf-token", (req, res) => {
res.json({
csrfToken: req.csrfToken(),
});
});
// Protected route
app.post("/api/transfer", csrfProtection, (req, res) => {
const { amount, toAccount } = req.body;
// Process transfer
res.json({
message: "Transfer successful",
amount,
toAccount,
});
});
// Error handler for CSRF errors
app.use((err, req, res, next) => {
if (err.code === "EBADCSRFTOKEN") {
return res.status(403).json({
error: "csrf_error",
message: "Invalid CSRF token",
});
}
next(err);
});
module.exports = { CSRFProtection, csrfProtection };Origin and Referer Validation
Origin and Referer Validation
// origin-validation.js
function validateOrigin(req, res, next) {
const allowedOrigins = ["https://example.com", "https://app.example.com"];
const origin = req.headers.origin;
const referer = req.headers.referer;
// Check Origin header
if (origin && !allowedOrigins.includes(origin)) {
return res.status(403).json({
error: "invalid_origin",
});
}
// Check Referer header as fallback
if (!origin && referer) {
const refererUrl = new URL(referer);
if (!allowedOrigins.includes(refererUrl.origin)) {
return res.status(403).json({
error: "invalid_referer",
});
}
}
next();
}
// Apply to state-changing routes
app.use("/api/*", validateOrigin);Python Flask CSRF Protection
Python Flask CSRF Protection
# csrf_protection.py
from flask import Flask, session, request, jsonify
from flask_wtf.csrf import CSRFProtect, generate_csrf, validate_csrf
from functools import wraps
import secrets
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['WTF_CSRF_TIME_LIMIT'] = 3600 # 1 hour
app.config['WTF_CSRF_SSL_STRICT'] = True
csrf = CSRFProtect(app)
# Cookie configuration
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Strict'
)
@app.before_request
def csrf_protect():
"""Validate CSRF token for state-changing methods"""
if request.method in ['POST', 'PUT', 'DELETE', 'PATCH']:
token = request.headers.get('X-CSRF-Token') or request.form.get('csrf_token')
if not token:
return jsonify({'error': 'CSRF token missing'}), 403
try:
validate_csrf(token)
except:
return jsonify({'error': 'Invalid CSRF token'}), 403
@app.route('/api/csrf-token', methods=['GET'])
def get_csrf_token():
"""Provide CSRF token to clients"""
token = generate_csrf()
return jsonify({'csrfToken': token})
@app.route('/api/transfer', methods=['POST'])
def transfer_funds():
"""Protected endpoint"""
data = request.get_json()
return jsonify({
'message': 'Transfer successful',
'amount': data.get('amount')
})
# Custom CSRF decorator
def require_csrf(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if request.method in ['POST', 'PUT', 'DELETE']:
token = request.headers.get('X-CSRF-Token')
if not token:
return jsonify({'error': 'CSRF token required'}), 403
try:
validate_csrf(token)
except:
return jsonify({'error': 'Invalid CSRF token'}), 403
return f(*args, **kwargs)
return decorated_function
@app.route('/api/sensitive-action', methods=['POST'])
@require_csrf
def sensitive_action():
return jsonify({'message': 'Action completed'})
if __name__ == '__main__':
app.run(ssl_context='adhoc')#!/bin/bash
# security-checklist.sh - Generate a security review checklist
# Usage: ./security-checklist.sh [--output checklist.md]
set -euo pipefail
OUTPUT="${{1:-/dev/stdout}}"
cat > "$OUTPUT" << 'CHECKLIST'
# Security Review Checklist
## Authentication & Authorization
- [ ] All endpoints require authentication
- [ ] Role-based access control implemented
- [ ] Session management is secure
## Input Validation
- [ ] All user inputs are validated
- [ ] SQL injection prevention
- [ ] XSS prevention
## Data Protection
- [ ] Sensitive data encrypted at rest
- [ ] Sensitive data encrypted in transit
- [ ] PII handling compliant
## TODO: Add domain-specific security checks
CHECKLIST
echo "Checklist generated: $OUTPUT" >&2
Related skills
How it compares
Use csrf-protection for cookie-session web apps; OAuth-only Bearer APIs without browser cookies rarely need synchronizer-token CSRF flows.
FAQ
What CSRF patterns does csrf-protection document?
csrf-protection covers synchronizer tokens, double-submit cookies, SameSite cookie attributes, and Origin or Referer validation with five framework-specific reference guides for Express, Flask, and frontend AJAX integration.
What token format does the csrf-protection quick-start use?
csrf-protection demonstrates crypto.randomBytes(32) converted to hex strings with a one-hour tokenExpiry stored per session, plus middleware integration guidance for state-changing routes.