Security

Two-Factor Authentication (2FA) for SSH, Web Apps, and Panels

May 14, 2026

Back to Blog

Why Passwords Alone Are Not Enough

Passwords are the oldest form of digital authentication, and they are fundamentally broken. According to Verizon's Data Breach Investigations Report, over 80% of hacking-related breaches involve stolen or weak passwords. People reuse passwords across services, choose predictable combinations, and fall for phishing attacks that harvest credentials. Even strong, unique passwords can be stolen through database breaches, keyloggers, or man-in-the-middle attacks.

Two-factor authentication (2FA) addresses this by requiring a second proof of identity in addition to the password. Even if an attacker steals your password, they cannot log in without the second factor. It is one of the most effective security measures available, and yet it remains underutilized — most estimates suggest that fewer than 30% of users enable 2FA when it is offered.

This guide covers implementing 2FA across your infrastructure: SSH servers, web applications, and control panels. We will look at TOTP (time-based one-time passwords), hardware security keys, backup codes, and recovery strategies.

What you will learn: Types of 2FA (TOTP, FIDO2/WebAuthn, SMS), setting up Google Authenticator for SSH, PAM configuration, implementing TOTP in web applications, hardware security keys (YubiKey), backup codes, recovery strategies, and common mistakes to avoid.

Types of Two-Factor Authentication

Not all 2FA methods are created equal. They fall into three categories based on the authentication factor they use:

MethodFactor TypeSecurity LevelConvenience
TOTP (Google Authenticator, Authy)Something you have (phone)HighGood
FIDO2/WebAuthn (YubiKey, passkeys)Something you have (hardware key)HighestGood
SMS codesSomething you have (phone number)ModerateGood
Email codesSomething you have (email access)ModerateFair
Push notificationsSomething you have (phone)HighExcellent
Avoid SMS-only 2FA: SMS is vulnerable to SIM swapping attacks, where an attacker convinces your mobile carrier to transfer your phone number to their SIM card. This has been used in high-profile attacks against cryptocurrency holders and tech executives. SMS is better than no 2FA, but TOTP or hardware keys are significantly more secure.

How TOTP Works

TOTP (Time-based One-Time Password) is the most widely used 2FA method. Understanding how it works helps you implement it correctly and troubleshoot issues.

Server generates secret key
User scans QR code
App stores secret
Every 30 seconds: HMAC-SHA1(secret, time) = 6-digit code

The process works like this: the server generates a random secret key and shares it with the user (usually via a QR code). Both the server and the authenticator app know the secret. Every 30 seconds, both sides independently compute a 6-digit code by running HMAC-SHA1 on the secret key combined with the current Unix timestamp divided by 30. Because they share the same secret and the same clock, they produce the same code. The server accepts codes within a small time window (typically plus or minus one 30-second interval) to account for clock drift.

Setting Up 2FA for SSH with Google Authenticator

Securing SSH with TOTP is one of the highest-impact security improvements you can make. Here is the complete setup process:

1
Install the PAM module
apt update && apt install libpam-google-authenticator -y
2
Run the setup for your user
google-authenticator Do you want authentication tokens to be time-based (y/n) y Your new secret key is: JBSWY3DPEHPK3PXP Your verification code is 123456 Your emergency scratch codes are: 12345678 87654321 11223344 44332211 99887766 Do you want to disallow multiple uses? (y/n) y Do you want to increase the time skew window? (y/n) n Do you want to enable rate-limiting? (y/n) y

Scan the QR code (displayed in the terminal) with your authenticator app. Save the emergency scratch codes in a secure location — they are your lifeline if you lose your phone.

3
Configure PAM — Edit /etc/pam.d/sshd and add the Google Authenticator module:
# /etc/pam.d/sshd # Add this line AFTER @include common-auth auth required pam_google_authenticator.so nullok

The nullok flag allows users who have not set up 2FA to still log in with just their password. Remove nullok once all users have configured their authenticator apps to enforce 2FA for everyone.

4
Configure SSHD — Edit /etc/ssh/sshd_config:
# /etc/ssh/sshd_config ChallengeResponseAuthentication yes UsePAM yes # Require both password and TOTP AuthenticationMethods keyboard-interactive
5
Restart SSH and test
systemctl restart sshd # Test from another terminal (keep current session open!) ssh user@your-server Password: ******** Verification code: 123456
Critical warning: Always test 2FA from a new SSH session while keeping your current session open. If you misconfigure PAM, you could lock yourself out of the server permanently. Keep a backup SSH session running until you have confirmed the new login flow works.

SSH Key + TOTP (Most Secure)

For maximum security, require both an SSH key and a TOTP code:

# /etc/ssh/sshd_config PubkeyAuthentication yes AuthenticationMethods publickey,keyboard-interactive

This requires users to first authenticate with their SSH key, then enter their TOTP code. Even if an attacker steals the private key, they still need the TOTP code to log in.

Implementing TOTP in Web Applications

Adding TOTP to your web application involves four steps: generating a secret, displaying a QR code, verifying codes, and managing backup codes.

Step 1: Generate a Secret Key

// Node.js example using speakeasy const speakeasy = require('speakeasy'); const QRCode = require('qrcode'); // Generate a secret const secret = speakeasy.generateSecret({ name: 'MyApp ([email protected])', issuer: 'MyApp', length: 32 }); // Store secret.base32 in the database (encrypted!) console.log(secret.base32); // e.g., "JBSWY3DPEHPK3PXP..." // Generate QR code for the user to scan const qrDataUrl = await QRCode.toDataURL(secret.otpauth_url); // Send qrDataUrl to the frontend to display as an img src

Step 2: Display Setup Page

Show the user a QR code and the secret key (for manual entry) along with a verification input. The user scans the QR code with their authenticator app and enters the current code to verify the setup is working before you enable 2FA on their account.

Step 3: Verify TOTP Codes

// Verify a code submitted by the user const isValid = speakeasy.totp.verify({ secret: user.totpSecret, // From database encoding: 'base32', token: submittedCode, // From user input window: 1 // Allow 1 step before/after (30 sec tolerance) }); if (isValid) { // Grant access } else { // Reject — wrong code }

Step 4: Generate Backup Codes

const crypto = require('crypto'); function generateBackupCodes(count = 10) { const codes = []; for (let i = 0; i < count; i++) { const code = crypto.randomBytes(4).toString('hex'); codes.push(code); } return codes; } // Store HASHED backup codes in the database // Show plain codes to user ONCE for them to save const codes = generateBackupCodes(); const hashedCodes = codes.map(c => bcrypt.hashSync(c, 10)); // Save hashedCodes to database // Show codes to user: ["a1b2c3d4", "e5f6g7h8", ...]
Important: Backup codes must be stored as hashed values in the database, just like passwords. When a user enters a backup code, hash it and compare against the stored hashes. Each backup code should be single-use — delete it after successful use.

PHP Implementation

// PHP example using pragmarx/google2fa composer require pragmarx/google2fa-laravel composer require bacon/bacon-qr-code <?php use PragmaRX\Google2FA\Google2FA; $google2fa = new Google2FA(); // Generate secret $secret = $google2fa->generateSecretKey(32); // Generate QR code URL $qrCodeUrl = $google2fa->getQRCodeUrl( 'MyApp', $user->email, $secret ); // Verify code $valid = $google2fa->verifyKey($user->totp_secret, $inputCode, 1);

Hardware Security Keys (FIDO2/WebAuthn)

Hardware security keys like YubiKey provide the strongest form of 2FA. They are phishing-resistant because the key is cryptographically bound to the specific website domain — a phishing site on a different domain cannot trigger the key.

Advantages

  • Phishing-resistant (domain-bound)
  • No codes to type (touch-to-authenticate)
  • Cannot be remotely intercepted
  • No batteries or connectivity needed
  • Works offline

Considerations

  • Hardware cost (~$25-$50 per key)
  • Users need physical key present
  • Should have a backup key or alternative method
  • Limited browser/platform support for some features
  • Risk of losing the key

WebAuthn Implementation

WebAuthn is the browser API for FIDO2 hardware keys and platform authenticators (fingerprint, Face ID). Here is a simplified registration flow:

// Frontend: Register a new credential const credential = await navigator.credentials.create({ publicKey: { challenge: serverChallenge, rp: { name: "MyApp", id: "myapp.com" }, user: { id: userId, name: "[email protected]", displayName: "John Doe" }, pubKeyCredParams: [ { alg: -7, type: "public-key" }, // ES256 { alg: -257, type: "public-key" } // RS256 ], authenticatorSelection: { authenticatorAttachment: "cross-platform", userVerification: "preferred" }, timeout: 60000 } }); // Send credential to server for verification and storage

Recovery Strategies: What If You Lose Your 2FA Device?

The biggest practical challenge with 2FA is account recovery. If a user loses their phone, their authenticator app, or their hardware key, they need a way back in. Here are the recommended recovery strategies:

StrategySecurityUsabilityNotes
Backup codesHighGood10 single-use codes, stored securely by user
Second hardware keyHighestFairRegister two keys, keep one in a safe place
Recovery emailModerateGoodSend a recovery link to a secondary email
Admin resetModerateFairContact support with identity verification
No recoveryHighestPoorAccount is permanently locked (some crypto wallets)
Panelica tip: Panelica has built-in TOTP two-factor authentication for all panel users. Enable 2FA from your profile settings — it works with Google Authenticator, Authy, or any TOTP-compatible app. Backup codes are automatically generated during setup.

2FA for Database Access

Database servers can also benefit from 2FA. While direct database 2FA is not common, you can implement it through the connection path:

Approach: SSH Tunnel with 2FA

Instead of exposing database ports to the network, require all database connections to go through an SSH tunnel. Since SSH already has 2FA configured (as we set up earlier), database access automatically requires two factors.

# SSH tunnel to database (requires 2FA login) ssh -L 3307:127.0.0.1:3306 user@db-server Password: ******** Verification code: 123456 # Connect to database through the tunnel mysql -h 127.0.0.1 -P 3307 -u dbuser -p

Common 2FA Mistakes to Avoid

SMS as the only 2FA method

SMS is vulnerable to SIM swapping and interception. Always offer TOTP or hardware keys as primary options and use SMS only as a fallback.

No backup codes provided

If users lose their device and have no backup codes, they are permanently locked out. Always generate and display backup codes during 2FA setup.

Storing TOTP secrets in plain text

TOTP secrets must be encrypted at rest in your database. If an attacker gains database access and secrets are in plain text, they can generate valid codes for every user.

No rate limiting on code entry

TOTP codes are only 6 digits (1 million combinations). Without rate limiting, an attacker could brute-force the current code within the 30-second window. Limit attempts to 3-5 per minute.

2FA Implementation Checklist

  • TOTP secret generated with sufficient entropy (minimum 160 bits)
  • QR code and manual entry key displayed during setup
  • User verifies a code before 2FA is activated
  • Backup codes generated and shown (10 codes minimum)
  • Backup codes stored as hashed values in database
  • TOTP secrets encrypted at rest in database
  • Rate limiting on code verification (max 5 attempts per minute)
  • Time window tolerance set (1 step = 30 seconds before/after)
  • Recovery flow documented and tested
  • SSH 2FA tested before closing current session
  • Users educated about saving backup codes securely
  • Admin override available for locked-out users (with identity verification)

Wrapping Up

Two-factor authentication is the single most impactful security improvement you can make to any system. A stolen password alone becomes useless, phishing attacks are largely neutralized (especially with hardware keys), and brute force attacks become practically impossible.

The implementation priority should be: SSH access first (it is the keys to the kingdom), then admin/panel accounts, then user accounts on web applications. Use TOTP as the standard method — it is free, widely supported, and does not depend on phone service. Offer hardware keys as an option for high-security accounts. Always provide backup codes, always encrypt secrets at rest, and always rate-limit verification attempts.

The hardest part of 2FA is not the technology — it is getting users to actually enable it. Make the setup process as smooth as possible, explain the risks of password-only authentication, and consider making 2FA mandatory for all administrative accounts. The small inconvenience of entering a 6-digit code is trivial compared to the devastating consequences of a compromised account.

Share:
Built for 2026, not 2002.