Hardware security keys (YubiKey, Titan): when software MFA isn't enough

A TOTP code from an authenticator app is orders of magnitude better than SMS. But it still fails against a well-executed phishing attack. A user who is deceived into entering their one-time code on a fake login page gives the attacker a valid 30-second window to replay that credential against the real site. Hardware security keys using FIDO2/WebAuthn solve this class of attack at the protocol level by binding the credential to the exact origin that registered it. There is no OTP to intercept.

The MFA hierarchy: SMS, TOTP, and FIDO2

Understanding why hardware keys are stronger requires understanding what each factor actually proves and what attacks it is immune to.

SMS one-time codes are the weakest form of MFA in common use. They are vulnerable to SIM-swap attacks, SS7 interception at the carrier level, and real-time phishing where an attacker proxies the code before it expires. NIST deprecated SMS as an authenticator in SP 800-63B for federal systems, though it remains widely deployed in consumer products because it requires no app installation.

TOTP (RFC 6238) improves significantly over SMS by eliminating carrier involvement. The shared secret lives on the user's device, and there is no network path for interception. The remaining exposure is phishing: if a user is on a convincing clone of your login page, they will enter a valid TOTP code just as readily as a password. That code is usable for the remaining seconds in its window.

FIDO2 hardware keys are phishing-resistant by design. During registration, the key generates a credential scoped to the origin (rpId) of the registering site. During authentication, the browser passes the origin to the key, which signs the challenge only if the origin matches. A phishing site at app-mybank.com will never receive a valid signature for mybank.com, even if the user plugs in their key and presses the button.

Attestation certificates and what they prove

When a FIDO2 authenticator creates a new credential, it can produce an attestation statement — a signature from the device manufacturer's attestation CA chain that proves the credential was generated inside a genuine piece of hardware with specific security properties. Relying parties can verify this chain against the FIDO Metadata Service (MDS) to confirm the authenticator model and its certification level (L1, L2, L3).

// Verifying attestation during WebAuthn registration
import { verifyRegistrationResponse } from '@simplewebauthn/server';

const verification = await verifyRegistrationResponse({
  response: registrationResponse,
  expectedChallenge: session.challenge,
  expectedOrigin: 'https://app.example.com',
  expectedRPID: 'example.com',
  // Require verifiable attestation — not 'none'
  requireUserVerification: true,
});

const { verified, registrationInfo } = verification;

if (verified && registrationInfo) {
  const {
    aaguid,           // authenticator model identifier
    fmt,              // attestation format: 'packed', 'tpm', 'fido-u2f', etc.
    credentialPublicKey,
    credentialID,
    counter,
  } = registrationInfo;

  // Look up AAGUID in FIDO MDS to get device metadata
  const metadata = await fidoMDS.getMetadataStatement(aaguid);
  console.log('Authenticator:', metadata?.description);
  console.log('Certification level:', metadata?.authenticatorGetInfo?.certifications);

  await db.credentials.create({
    userId,
    credentialId: Buffer.from(credentialID).toString('base64url'),
    publicKey: Buffer.from(credentialPublicKey).toString('base64url'),
    counter,
    aaguid,
    attestationFormat: fmt,
    createdAt: new Date(),
  });
}

For most consumer applications, attestation verification is optional. For enterprise deployments where you want to enforce that users register only specific hardware key models (YubiKey 5 Series, Google Titan Security Key), checking the AAGUID against an allow-list during registration prevents software authenticators or lower-security keys from being enrolled.

Resident keys vs server-side credential storage

The FIDO2 specification supports two storage models for credentials. In the traditional model, the credential ID (an opaque blob generated by the key) is stored server-side and provided to the authenticator during the authentication ceremony. The key looks up the private key material based on this ID. The credential ID can be long — 64 to 128 bytes — and the user must have their credential ID provided before they can authenticate.

Resident keys (also called discoverable credentials) store the credential on the key itself in protected storage, indexed by the relying party ID. During authentication, the user can authenticate without providing a username — the key enumerates its stored credentials for the RP and presents them. This enables passwordless flows where the user touches the key and is immediately authenticated.

// Registration: request resident key creation
const registrationOptions = await generateRegistrationOptions({
  rpName: 'Example Corp',
  rpID: 'example.com',
  userID: userId,
  userName: userEmail,
  authenticatorSelection: {
    // Prefer platform authenticator or cross-platform key
    authenticatorAttachment: 'cross-platform',
    // Require on-device verification (PIN or biometric)
    userVerification: 'required',
    // Request resident key storage on the authenticator
    residentKey: 'required',
    requireResidentKey: true,
  },
  attestationType: 'direct',
});

// Authentication: no username required for discoverable credentials
const authOptions = await generateAuthenticationOptions({
  rpID: 'example.com',
  // Empty allowCredentials triggers discoverable credential flow
  allowCredentials: [],
  userVerification: 'required',
});

The tradeoff is key storage limits. YubiKey 5 Series supports 25 resident credentials. The newer YubiKey 5.7 firmware supports 100. Google Titan keys support 250. For enterprise deployments with multiple services, users may exhaust resident key slots. Server-side credentials have no such limit, at the cost of requiring a username before authentication can begin.

Enterprise provisioning

Distributing hardware keys at scale requires thinking beyond the individual registration flow. Enterprises typically need to:

  • Pre-enroll keys before distribution so users do not need to complete an enrollment ceremony themselves
  • Manage key inventory and map serial numbers to employees in an asset management system
  • Handle lost or damaged keys with a secure recovery path that does not defeat the phishing resistance of the primary factor
  • Enforce key-only authentication for privileged accounts (domain admins, billing contacts) while allowing TOTP for standard accounts
  • Integrate with directory services so key policy follows group membership

YubiKey Manager CLI (ykman) supports bulk configuration. You can script PIN setting, FIDO2 credential management, and attestation certificate export for an entire shipment of keys before they leave your hands.

# Bulk configure YubiKeys via ykman
# Set FIDO2 PIN on each key before distribution
ykman --device $SERIAL fido access change-pin --new-pin "$GENERATED_PIN"

# Export attestation certificate for inventory record
ykman --device $SERIAL fido attestation export-certificate \
  --output "attestation_${SERIAL}.pem"

# Verify the certificate chain against Yubico root CA
openssl verify \
  -CAfile yubico-root-ca.pem \
  -untrusted yubico-intermediate.pem \
  "attestation_${SERIAL}.pem"
Recovery from a lost hardware key is the weakest link in hardware-key-enforced MFA. A backup code flow, secondary key registration, or supervised reset via HR verification are all valid recovery paths. The worst option is falling back to email-based recovery — that just shifts the attack surface to email account compromise, which may have weaker security than what you are trying to protect.

Comparing key form factors

YubiKey 5 NFC is the most common enterprise choice: USB-A and NFC in a single key, compatible with Windows, macOS, Linux, iOS, and Android. YubiKey 5C NFC replaces USB-A with USB-C. For environments that require only USB and want a lower profile, the YubiKey 5 Nano fits flush in a port. Google Titan Security Keys are similar FIDO2 devices with Google-managed firmware updates, often preferred when there is policy around third-party firmware.

For high-security environments — privileged access workstations, infrastructure access, financial transaction signing — the YubiKey 5 FIPS series carries FIPS 140-2 Level 2 certification. The attestation certificates on FIPS keys include the certification reference, which you can verify and log at enrollment time.

Requiring hardware keys for specific roles

Not every user needs a hardware key. A tiered policy is practical: standard users can use any enrolled MFA factor, while privileged users (admins, billing managers, on-call engineers with production access) must use a FIDO2 authenticator. Enforce this by checking the credential type at authentication time and gating access to privileged routes.

// Middleware: require FIDO2 for privileged routes
function requireHardwareKey(req, res, next) {
  const session = req.session;
  if (!session?.mfaFactor || session.mfaFactor !== 'fido2') {
    return res.status(403).json({
      error: 'hardware_key_required',
      message: 'This action requires authentication with a hardware security key.',
    });
  }
  // Optional: check authenticator attachment was 'cross-platform'
  // to distinguish hardware keys from platform authenticators (Touch ID)
  if (session.authenticatorAttachment === 'platform' && req.route.requiresHardwareKey) {
    return res.status(403).json({
      error: 'cross_platform_authenticator_required',
    });
  }
  next();
}

router.delete('/org/delete', requireHardwareKey, deleteOrgHandler);
router.post('/billing/update-payment', requireHardwareKey, updatePaymentHandler);

Hardware keys represent a meaningful step up in security posture for accounts that warrant the investment. The phishing resistance property is not achievable through software means — it is intrinsic to how the protocol binds credentials to origins. For enterprises where a compromised admin account could affect all tenants, requiring hardware keys for admin users is one of the highest-leverage security controls available.

← Back to blog Try Bastionary free →