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.
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.
Require current password verification before executing sensitive account changes. This ensures a stolen session alone is insufficient for full account takeover.
// 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.
ID: saas-authorization.admin-privilege.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.
// 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.