Two-Factor Authentication (2FA)
debt(d7/e7/b5/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints field explicitly states 'automated: no' and describes the code pattern as 'Authentication flow with no second factor check after password verification' — there is no listed tool that catches missing 2FA, no linter rule, and no SAST signature. A reviewer must manually trace the authentication flow to detect absence of a second factor or a weak SMS-only implementation.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix mentions integrating a TOTP library and generating backup codes, but the common_mistakes reveal systemic gaps: fixing all bypass vectors (account recovery, re-verification for high-risk actions, plaintext TOTP secret storage) touches authentication flows, database schema, and multiple application entry points. This is not a single-file patch — it requires coordinated changes across the auth layer, recovery flows, and sensitive action gates.
Closest to 'persistent productivity tax' (b5). 2FA applies only to web contexts (per applies_to) but its authentication and re-verification logic must be threaded through every privileged route, account recovery path, and high-risk action. It doesn't reshape the entire architecture (ruling out b7+), but it creates an ongoing tax whenever new privileged features are added, as each must integrate the second-factor check.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field directly states that developers wrongly assume 'any 2FA method provides equal protection' — SMS is widely deployed but vulnerable to SIM-swapping and SS7 interception. This is a well-known gotcha in the security community but genuinely surprises developers who implement SMS 2FA thinking it provides strong protection. The additional common mistake of bypass via weak recovery flows compounds the trap.
Also Known As
TL;DR
Explanation
2FA adds a second authentication layer — something the user has (TOTP app, hardware key) — to something they know (password). Even if an attacker obtains valid credentials through phishing or a data breach, they cannot authenticate without the second factor. Implementation considerations include: using TOTP (RFC 6238) rather than SMS (vulnerable to SIM-swapping), providing recovery codes, enforcing 2FA on admin accounts, and protecting the 2FA setup flow from being bypassed.
Diagram
sequenceDiagram
participant U as User
participant APP as Application
participant TOTP as TOTP / SMS
U->>APP: Username and Password
APP-->>APP: Password correct
APP-->>U: Request 2FA code
APP->>TOTP: Generate or send OTP
U->>APP: Submit OTP
APP-->>APP: Verify OTP time-based
APP-->>U: Authenticated successfully
Note over U,APP: Even if password stolen<br/>attacker needs the second factor
Common Misconception
Why It Matters
Common Mistakes
- SMS-based 2FA as the only option — SIM swapping defeats it; offer TOTP or hardware keys.
- 2FA bypass via account recovery — a weak recovery flow lets attackers skip 2FA entirely.
- Not requiring 2FA re-verification for high-risk actions (password change, payment method update).
- Storing TOTP secrets in plaintext in the database — should be encrypted at rest.
Avoid When
- Do not make 2FA optional for high-privilege accounts — treat it as mandatory for admins.
- Do not implement 2FA over SMS for high-security contexts — SS7 attacks and SIM swapping make it interceptable.
When To Use
- Enable 2FA for all admin accounts, privileged roles, and any account with access to sensitive data.
- Use TOTP (authenticator app) as the default second factor — more secure than SMS which is vulnerable to SIM swap.
Code Examples
// 2FA check bypassable via recovery:
if ($user->hasTwoFactor() && !$this->verifyTotp($code)) {
return $this->offerRecovery(); // Recovery flow has weaker verification
// Attacker: 'forgot my 2FA device' → answers security questions → bypasses 2FA
}
// TOTP (Google Authenticator compatible) — use spomky-labs/otphp
use OTPHP\TOTP;
// Setup — generate secret and QR code URI
$totp = TOTP::generate();
$totp->setLabel($user->email);
$totp->setIssuer('MyApp');
$secret = $totp->getSecret(); // store encrypted in DB
$uri = $totp->getProvisioningUri(); // display as QR code
// Verify — check the 6-digit code
function verifyTotp(string $secret, string $code): bool {
$totp = TOTP::createFromSecret($secret);
return $totp->verify($code, null, 1); // ±1 time window tolerance
}
// Recovery codes — generate 8 codes, hash and store, show once
$codes = array_map(fn() => bin2hex(random_bytes(5)), range(1, 8));