What it is#
Multi-factor authentication requires evidence from more than one factor category, such as something the user knows and something the user possesses. Two passwords or a password plus security questions are still one factor category.
MFA is missing when sensitive accounts or actions rely on a password alone, when enrollment is optional despite policy, or when alternate login, recovery, API, and legacy-protocol paths bypass the second factor.
Not all MFA is equally strong. SMS and one-time codes improve resistance to password reuse but remain phishable. Passkeys and security keys based on WebAuthn provide stronger phishing resistance because authentication is bound to the legitimate origin.
Why it matters#
- A reused or phished password immediately becomes an authenticated session.
- Password spraying can compromise remote access, email, cloud, and administrator accounts.
- Privileged users remain exposed to targeted phishing and help-desk social engineering.
- Attackers can enroll their own authenticator after logging in with only a password.
- Weak recovery or legacy authentication can bypass otherwise strong MFA.
- High-risk actions may be performed from an old session without fresh proof of user presence.
Medium for low-impact accounts with strong compensating controls. High to critical for administrators, remote access, email, finance, source control, cloud control planes, and systems containing sensitive client data. Actual severity depends on context: who is affected, what data or actions are reachable, and existing controls.
Examples#
// Vulnerable: a password-only session can perform a sensitive action.
app.post('/account/api-keys', requireLogin, async (req, res) => {
const key = await createApiKey(req.user.id);
res.status(201).json(key);
});
// Fixed: require a recent trusted MFA assertion for the sensitive action.
function requireRecentMfa(maxAgeMs) {
return (req, res, next) => {
const usedMfa = req.session.authMethods?.includes('webauthn');
const recent = Date.now() - req.session.authenticatedAt <= maxAgeMs;
if (!usedMfa || !recent) return res.status(403).json({ reauthenticate: true });
next();
};
}
app.post('/account/api-keys',
requireLogin,
requireRecentMfa(5 * 60 * 1000),
async (req, res) => {
const key = await createApiKey(req.user.id);
res.status(201).json(key);
});
The session values must come from a trusted, server-validated authentication event, not from client-supplied claims.
How to fix it#
1. Require MFA for sensitive access. Enforce it for administrators, remote access, cloud consoles, email, code repositories, finance, support tooling, and applications holding regulated or client data.
2. Prefer phishing-resistant authenticators. Offer and prioritize passkeys or hardware security keys using WebAuthn. Use code-based methods only where stronger authenticators are not practical.
3. Enforce policy server-side. The identity provider and application must reject sessions that do not meet the required authentication context. Hiding a feature until enrollment is not enforcement.
4. Cover every authentication path. Disable or protect legacy protocols, app passwords, local fallback accounts, support impersonation, CLI access, API tokens, recovery, and service-provider initiated login.
5. Require step-up authentication. Obtain a fresh MFA assertion before credential changes, authenticator enrollment, recovery changes, privilege grants, payouts, key creation, and export of sensitive data.
6. Secure enrollment and replacement. Require an existing strong factor or controlled identity proofing. Notify users when authenticators are added or removed and maintain auditable recovery procedures.
7. Provide safe recovery. Issue single-use recovery codes, store them like passwords, and avoid knowledge-based questions. High-risk recovery should require human review or stronger identity evidence.
8. Monitor MFA events. Alert on repeated challenges, new authenticator enrollment, factor downgrades, impossible travel, unusual devices, and recovery activity.
How to test for it#
- Build an account and role matrix covering normal users, administrators, service accounts, support access, and break-glass accounts.
- Verify password-only sessions cannot reach protected applications or sensitive functions.
- Test alternate paths: mobile, API, CLI, legacy protocols, identity-provider initiated login, local login, recovery, and remembered-device flows.
- Attempt to add, replace, or remove an authenticator without a recent strong assertion. The application must require a protected recovery path.
- Verify session claims represent the method actually used and are validated against the expected issuer and audience.
- Test step-up expiration and confirm an old MFA event cannot authorize a newly sensitive action indefinitely.
- Review break-glass account storage, monitoring, expiration, and post-use rotation without disabling emergency access during testing.
- Add automated authorization tests for required authentication context on every privileged route.
References#
- CWE-308: Use of Single-factor Authentication ↗ (opens in a new tab)cwe.mitre.org
- NIST SP 800-63B: Authentication and Authenticator Management ↗ (opens in a new tab)pages.nist.gov
- OWASP Multifactor Authentication Cheat Sheet ↗ (opens in a new tab)cheatsheetseries.owasp.org
- OWASP Authentication Cheat Sheet ↗ (opens in a new tab)cheatsheetseries.owasp.org
- OWASP Top 10 A07:2021 Identification and Authentication Failures ↗ (opens in a new tab)owasp.org