Step-up uses equal or higher strength than initial login
Why it matters
Step-up authentication is only meaningful if the challenge it presents is at least as hard to satisfy as the initial login. Downgrading from a password+biometric login to a plain OTP step-up means the attacker who cannot replicate the biometric at login can instead wait for the step-up prompt and satisfy it with a weaker factor. CWE-287 (Improper Authentication) and NIST 800-63B AAL2 both require that step-up methods match or exceed the assurance level of the authenticating session. OWASP A07 identifies authentication downgrade as a category-defining failure. In practice, a strength mismatch turns step-up into a false signal — it appears to add security while actually lowering the bar.
Severity rationale
Medium because strength downgrade does not eliminate the step-up challenge but renders it easier to satisfy than the original login, reducing the additional security margin that step-up is supposed to provide.
Remediation
Define strength levels explicitly in src/lib/authStrength.ts and enforce that the step-up method meets or exceeds the login method's level:
const STRENGTH: Record<string, number> = {
'password': 1,
'otp': 1,
'password+otp': 2,
'biometric': 2,
'password+biometric': 3
};
export async function requireStepUpAuth(
session: Session,
minOverride?: number
): Promise<boolean> {
const loginStrength = STRENGTH[session.authMethod] ?? 1;
const requiredStrength = Math.max(loginStrength, minOverride ?? 0);
const stepUpMethod = await promptStepUp(requiredStrength);
return STRENGTH[stepUpMethod] >= requiredStrength;
}
Never hard-code step-up to a fixed method without first querying the session's login strength — users who authenticated at level 3 must be challenged at level 3.
Detection
- ID:
step-up-strength-equivalent - Severity:
medium - What to look for: Enumerate all authentication methods available for login and step-up. Assign a strength level to each (e.g., password=1, OTP=1, password+OTP=2, biometric=2, password+biometric=3). Compare login strength vs. step-up strength. Quote the actual authentication methods found. A step-up method weaker than the initial login must not pass.
- Pass criteria: Step-up authentication strength level is at least 1 (on a scale of 1-3) and equal to or greater than the initial login authentication strength level. Count all auth methods — report the ratio even on pass (e.g., "Login strength: level 2 (password+OTP), step-up strength: level 2 (biometric) — meets minimum").
- Fail criteria: Step-up uses a weaker authentication method than login (step-up strength level < login strength level).
- Skip (N/A) when: No step-up authentication is configured (caught by step-up-sensitive-ops check) — cite the missing configuration found.
- Detail on fail:
"Login requires strength level 3 (password+biometric). Step-up is level 1 (OTP only) — below minimum." - Remediation: Define step-up strength requirements that match or exceed login strength (in
src/lib/authStrength.ts):// lib/authStrength.ts const authStrengthLevels = { 'password': 1, 'otp': 1, 'password+otp': 2, 'biometric': 2, 'password+biometric': 3 }; export async function requireStepUpAuth(session: Session, minStrength: number) { // Determine initial login strength const loginStrength = authStrengthLevels[session.authMethod]; // Require step-up of equal or higher strength const requiredStepUpStrength = loginStrength; const stepUpMethod = await promptStepUp(requiredStepUpStrength); return authStrengthLevels[stepUpMethod] >= requiredStepUpStrength; }
External references
- cwe · CWE-287 — Improper Authentication
- owasp:2021 · A07
- nist:rev4 · SP-800-63B §4.2 — Authenticator Assurance Level 2 — AAL2 authentication assurance requirements
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-session-security·automated