Claude Code Plugins

Community-maintained marketplace

Feedback

Implement Cross-Site Request Forgery (CSRF) protection using tokens, SameSite cookies, and origin validation. Use when building forms and state-changing operations.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name csrf-protection
description Implement Cross-Site Request Forgery (CSRF) protection using tokens, SameSite cookies, and origin validation. Use when building forms and state-changing operations.

CSRF Protection

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

Implementation Examples

1. 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 };

2. 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' });
});

3. 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')

4. 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>
  );
}

5. 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);

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

CSRF Protection Methods

  1. Synchronizer Token: Server-generated tokens
  2. Double Submit Cookie: Cookie and header match
  3. SameSite Cookies: Browser-level protection
  4. Custom Headers: X-Requested-With
  5. Origin Validation: Check request origin

Defense Layers

  • CSRF tokens implemented
  • SameSite cookies configured
  • Origin/Referer validation
  • Custom request headers
  • Token expiration
  • Secure cookie flags
  • HTTPS enforced

Resources