Skip to content
intermediatePhase 41 · Frontend Security

CSRF

Prevent Cross-Site Request Forgery with tokens and SameSite cookies.

30m
0 problems
Topic Progress0%

CSRF Attacks

CSRF Attacks

How CSRF Works

  1. User logs into bank.com, receives session cookie
  2. User visits malicious site
  3. Malicious site sends request to bank.com
  4. Browser automatically includes session cookie
  5. Bank processes request as if it's from the user

Example Attack

<!-- Malicious site -->
<form action="https://bank.com/transfer" method="POST">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="10000">
</form>
<script>document.forms[0].submit();</script>

Vulnerable Code

// Vulnerable: No CSRF protection
app.post('/transfer', (req, res) => {
  // Processes transfer without verifying origin
  const { to, amount } = req.body;
  transferMoney(req.user, to, amount);
  res.json({ success: true });
});

CSRF Targets

  • State-changing operations (POST, PUT, DELETE)
  • Authentication endpoints
  • Financial transactions
  • User settings changes

Safe Methods

  • GET should be safe (no side effects)
  • HEAD, OPTIONS are typically safe
  • POST, PUT, DELETE need CSRF protection

CSRF Protection

CSRF Protection

CSRF Tokens

// Server: Generate token
csrfProtection = require('csurf');
app.use(csrfProtection({ cookie: true }));

app.get('/form', (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

app.post('/process', (req, res) => {
  // Token is validated automatically
  processForm(req.body);
});

Frontend Token Usage

// React: Include token in forms
function TransferForm() {
  const [token, setToken] = useState('');

  useEffect(() => {
    fetch('/api/csrf-token')
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  return (
    <form method="POST" action="/transfer">
      <input type="hidden" name="_csrf" value={token} />
      <input name="to" />
      <input name="amount" type="number" />
      <button type="submit">Transfer</button>
    </form>
  );
}

SameSite Cookies

// Set SameSite cookie
res.cookie('session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict', // or 'lax'
});

// SameSite values:
// strict: Cookie only sent for same-site requests
// lax: Cookie sent for top-level navigation (safe default)
// none: Cookie sent for all requests (requires secure)

Origin/Referer Headers

// Server: Verify origin
app.post('/transfer', (req, res) => {
  const origin = req.headers.origin || req.headers.referer;
  
  if (!origin || !origin.startsWith('https://myapp.com')) {
    return res.status(403).json({ error: 'Invalid origin' });
  }

  // Process request
});

Double Submit Cookie

// Server: Set random token in cookie and require it in header
const csrfToken = crypto.randomBytes(32).toString('hex');
res.cookie('csrf-token', csrfToken, { sameSite: 'strict' });

// Client: Send token in header
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': getCookie('csrf-token'),
  },
  body: JSON.stringify(data),
});

Additional Protections

Additional Protections

CORS Configuration

// Server: Configure CORS
const cors = require('cors');

app.use(cors({
  origin: 'https://myapp.com',
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'X-CSRF-Token'],
}));

Custom Headers

// Client: Always send custom header for AJAX
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-Requested-With': 'XMLHttpRequest',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data),
});

// Server: Verify custom header
app.post('/api/transfer', (req, res) => {
  if (req.headers['x-requested-with'] !== 'XMLHttpRequest') {
    return res.status(403).json({ error: 'Invalid request' });
  }
});

Content-Type Validation

// Server: Only accept specific content types
app.post('/api/transfer', (req, res) => {
  if (req.headers['content-type'] !== 'application/json') {
    return res.status(415).json({ error: 'Unsupported content type' });
  }
});

Frame Protection

// Prevent framing (clickjacking)
app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
  next();
});

Best Practices

  1. Use SameSite cookies (strict or lax)
  2. Implement CSRF tokens for state-changing operations
  3. Validate Origin/Referer headers
  4. Use custom headers for AJAX requests
  5. Don't rely solely on CORS for CSRF protection
  6. Set Content-Type for API requests
  7. Use HTTPS everywhere

Testing CSRF Protection

describe('CSRF Protection', () => {
  it('should reject requests without CSRF token', async () => {
    const response = await request(app)
      .post('/transfer')
      .send({ to: 'attacker', amount: 1000 });

    expect(response.status).toBe(403);
  });

  it('should accept requests with valid CSRF token', async () => {
    const token = await getCsrfToken();
    const response = await request(app)
      .post('/transfer')
      .set('X-CSRF-Token', token)
      .send({ to: 'friend', amount: 100 });

    expect(response.status).toBe(200);
  });
});

Practice Problems

0/3solved
Build Cross-Site Request Forgery (CSRF) Component

Create a reusable React component implementing Cross-Site Request Forgery (CSRF). Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Cross-Site Request Forgery (CSRF) Testing

Write unit and integration tests for Cross-Site Request Forgery (CSRF) using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Cross-Site Request Forgery (CSRF) Performance

Optimize Cross-Site Request Forgery (CSRF) for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is CSRF?

Question 1 options

2. What is SameSite cookie attribute?

Question 2 options

3. What is the primary purpose of Cross-Site Request Forgery (CSRF)?

Question 3 options

4. What is a common mistake when implementing Cross-Site Request Forgery (CSRF)?

Question 4 options

Flashcards

Question

What is CSRF?

Answer

Cross-Site Request Forgery - tricking users into making unintended state-changing requests.

Question

What is SameSite cookie?

Answer

An attribute that controls when cookies are sent with cross-site requests.

Question

How do CSRF tokens work?

Answer

A random token is generated server-side and must be included in requests to verify origin.

Question

Which requests need CSRF protection?

Answer

State-changing requests: POST, PUT, DELETE (not GET which should be safe).

Question

What is Cross-Site Request Forgery (CSRF)?

Answer

Cross-Site Request Forgery (CSRF) is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.CSRF tricks users into making unintended requests
  • 2.Use SameSite cookies as primary defense
  • 3.CSRF tokens protect state-changing operations
  • 4.GET requests should be safe (no side effects)
  • 5.Validate Origin/Referer headers

Interview Tips

  • Explain how CSRF attacks work
  • Discuss SameSite cookie attribute
  • Know multiple CSRF protection methods

Cheat Sheet

CSRF Protection Cheat Sheet

Attack

  • User visits malicious site
  • Malicious site sends request to your app
  • Browser includes session cookie
  • App processes as legitimate request

Protection Methods

  1. CSRF Tokens
  2. SameSite cookies
  3. Origin/Referer validation
  4. Custom headers
  5. CORS configuration

SameSite Values

  • strict: Only same-site
  • lax: Top-level navigation
  • none: All requests (needs secure)

Best Practices

  • Use SameSite: strict or lax
  • CSRF tokens for state changes
  • Validate Origin header
  • Use custom headers for AJAX