# Login form has CSRF protection

- **Pattern:** `ab-002234` (`saas-authentication.auth-flow.login-csrf`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002234
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002234)

## Why it matters

Login CSRF (CAPEC-62) lets an attacker log a victim into an attacker-controlled account using a forged cross-site request. This enables account takeover indirectly: the victim, now logged into the attacker's account, may enter sensitive data that the attacker can later retrieve. CWE-352 (CSRF) on a login endpoint is classified under OWASP A01 (Broken Access Control); PCI-DSS v4.0 Req-6.2.4 explicitly lists cross-site request forgery among the software attacks that engineering practices must prevent. SameSite=Lax provides meaningful browser-enforced protection for most cases, but explicit CSRF token validation is required for custom endpoints that process sensitive actions on navigation.

## Severity rationale

Critical because login CSRF can silently associate a victim's activity with an attacker's account, enabling credential harvesting and session theft without any direct compromise of the victim's credentials.

## Remediation

If using NextAuth, CSRF protection is built into the credential callback — do not bypass the standard signin form flow. For custom endpoints, add explicit CSRF token validation:

```ts
import { csrf } from '@edge-csrf/nextjs'
const csrfProtect = csrf({ cookie: { secure: true } })

export async function POST(request: Request) {
  const csrfError = await csrfProtect(request)
  if (csrfError) return Response.json({ error: 'Invalid CSRF token' }, { status: 403 })
  // login logic
}
```

Verify session cookies have `SameSite=Lax` set (see the `session-tokens-secure` check) as a defense-in-depth layer. Do not rely on SameSite alone when you can add a CSRF token.

## Detection

- **ID:** `login-csrf`
- **Severity:** `critical`
- **What to look for:** Examine the login endpoint for CSRF protection. In NextAuth: CSRF protection is built-in via the `csrfToken` mechanism — verify the login form uses the `/api/auth/csrf` endpoint to retrieve the token. In custom endpoints: check for CSRF token validation middleware, `SameSite=Strict` or `SameSite=Lax` cookie enforcement, or `Origin`/`Referer` header validation. For cookie-based sessions, `SameSite=Lax` provides meaningful protection for top-level navigations. Count all instances found and enumerate each.
- **Pass criteria:** The login endpoint is protected against CSRF via one or more of: a CSRF token validated server-side, `SameSite=Strict` session cookies, or an auth library that includes built-in CSRF protection (NextAuth, Clerk). The protection covers the POST that establishes the session. At least 1 implementation must be confirmed.
- **Fail criteria:** Custom login endpoint accepts POST requests with no CSRF token validation and uses cookies with `SameSite=None` or unset `SameSite`. No Origin/Referer validation present.
- **Skip (N/A) when:** Application uses only JSON API authentication with bearer tokens (no cookies) — CSRF requires cookie-based sessions. Signal: auth is entirely stateless JWT with no session cookies.
- **Cross-reference:** The `session-tokens-secure` check verifies the cookie security flags that complement CSRF token validation.
- **Detail on fail:** `"Login endpoint at /api/auth/login accepts POST with session cookies but has no CSRF token, no Origin validation, and cookies use SameSite=None"`.
- **Remediation:** CSRF on a login endpoint allows an attacker to log victims into an attacker-controlled account (login CSRF), enabling account takeover indirectly. If using NextAuth, CSRF protection is already built in — verify you're using the standard signin flow:

  ```ts
  // NextAuth handles CSRF automatically via csrfToken
  // Your custom login form should POST to /api/auth/callback/credentials
  // with the csrfToken from /api/auth/csrf

  // For custom endpoints, validate CSRF token:
  import { csrf } from '@edge-csrf/nextjs'
  const csrfProtect = csrf({ cookie: { secure: true } })

  export async function POST(request: Request) {
    const csrfError = await csrfProtect(request)
    if (csrfError) return Response.json({ error: 'Invalid CSRF token' }, { status: 403 })
    // ... login logic
  }
  ```

  Setting `SameSite=Lax` on session cookies provides meaningful protection in modern browsers as a defense-in-depth measure.

## External references

- cwe CWE-352
- owasp A01
- capec CAPEC-62
- pci-dss Req-6.2.4

Taxons: access-control, injection-and-input-trust

HTML version: https://auditbuffet.com/patterns/ab-002234
