# Sensitive Operations Require Re-authentication

- **Pattern:** `ab-002269` (`saas-authorization.admin-privilege.sensitive-ops-reauth`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002269
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002269)

## Why it matters

Sensitive account operations — password change, email change, account deletion, MFA modification — that execute with only a valid session cookie are fully exploitable by session hijacking or CSRF. An attacker who steals a session token can change the account email, locking out the legitimate owner permanently. CWE-306 (Missing Authentication for Critical Function) and CWE-308 (Use of Single-Factor Authentication for a Critical Action) define this failure. OWASP A07 (Identification and Authentication Failures) covers it. NIST 800-53 IA-11 (Re-Authentication) explicitly requires fresh authentication for high-impact operations. Re-authentication limits the stolen-session blast radius to read-only access.

## Severity rationale

High because a stolen session cookie enables permanent account takeover through email address change without any additional credential, with no recovery path for the victim.

## Remediation

Require current password verification before executing sensitive account changes. This ensures a stolen session alone is insufficient for full account takeover.

```typescript
// app/api/account/change-password/route.ts
export async function POST(req: NextRequest) {
  const session = await auth();
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  const { currentPassword, newPassword } = await req.json();

  const user = await db.user.findUnique({ where: { id: session.user.id } });
  const isValid = await bcrypt.compare(currentPassword, user!.passwordHash);
  if (!isValid) return NextResponse.json({ error: 'Invalid current password' }, { status: 403 });

  const newHash = await bcrypt.hash(newPassword, 12);
  await db.user.update({ where: { id: session.user.id }, data: { passwordHash: newHash } });
}
```

For email changes, send a confirmation link to the current address rather than applying the change immediately — this gives the account owner a chance to block an unauthorized change.

## Detection

- **ID:** `sensitive-ops-reauth`
- **Severity:** `high`
- **What to look for:** Count all relevant instances and enumerate each. Find endpoints for high-impact account operations: password change, email address change, account deletion, payment method change, MFA configuration, API key generation with full permissions, data export. Check whether these endpoints require the user to re-confirm their current password or complete an additional identity challenge before the action is performed.
- **Pass criteria:** At least the most sensitive operations (password change, email change, account deletion) require the user to provide their current password or a fresh auth token as part of the request, not just a valid session cookie. At least 1 implementation must be verified.
- **Fail criteria:** Sensitive operations execute with only a valid session cookie — no current password, no OTP, no re-authentication. This means a session hijack or CSRF gives an attacker complete account takeover capability.
- **Skip (N/A) when:** The application has no user account settings, no password-based authentication, and no sensitive account operations.
- **Detail on fail:** `"Sensitive operations (password change, email change, account deletion) execute with session cookie alone. Session hijack or CSRF enables full account takeover."` (List the specific operations affected.)
- **Remediation:** Require current password verification before executing sensitive account changes. This limits the damage from session hijacking — an attacker with a stolen cookie cannot change the email address or delete the account without also knowing the password.

  ```typescript
  // app/api/account/change-password/route.ts
  export async function POST(req: NextRequest) {
    const session = await auth();
    if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

    const { currentPassword, newPassword } = await req.json();

    // Verify current password before allowing the change
    const user = await db.user.findUnique({ where: { id: session.user.id } });
    const isValid = await bcrypt.compare(currentPassword, user!.passwordHash);
    if (!isValid) return NextResponse.json({ error: 'Invalid current password' }, { status: 403 });

    // Current password verified — safe to proceed
    const newHash = await bcrypt.hash(newPassword, 12);
    await db.user.update({ where: { id: session.user.id }, data: { passwordHash: newHash } });
  }
  ```

  For email changes, consider sending a confirmation link to the **current** email address rather than immediately applying the change, so the account owner is notified.

## External references

- cwe CWE-306
- cwe CWE-308
- owasp A07
- nist IA-11

Taxons: access-control

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