Getting Started with YubiKey Security Key: A Beginner's Guide
A brief introduction to the YubiKey security key, common authentication use cases, and examples for using the key in app authentication.
Legacy MFA schemes like PIN, SMS/Email OTP, TOTP apps, and mobile push, are vulnerable to phishing attempts and malware. FIDO2-based hardware security keys like Google Titan and YubiKey are probably the only known phishing resistant technologies available today. In this post, we'll do a deep dive into YubiKey security keys, common authentication use cases, and interacting with the devices programmatically during FIDO2 registration and authentication.
What is YubiKey?
YubiKey is a hardware security key from Yubico, providing strong multi-factor authentication for a wide range of applications and services. YubiKey supports multiple authentication protocols, including U2F, FIDO2/WebAuthn, OpenPGP, OTP, and smart card, and ships in USB-A/C, NFC and Lightning form factors. The keys are IP68 rated, crush resistant, with no batteries or moving parts, making them really ideal for long term use. The most popular YubiKeys are USB-A/NFC, USB-C/NFC and USB-C/Lightning.
How is YubiKey Different from Google Titan?
Google Titan is also a hardware security key, developed by Google, and can be used to authenticate to Google, Google Cloud, and many other services that support FIDO standards. Titan security keys are compatible with the Advanced Protection Program, Google's strongest account security program, originally introduced to safeguard politicians, journalists, activists and others who are at a high risk of targeted online attacks, but now available to everyone. The Titan hardware chip includes firmware to help verify that the keys haven't been tampered with. And just like YubiKey, Titan keys also offer cryptographic proof that users are interacting with legitimate services once they have registered their security keys.
However, YubiKey has better support for SSH authentication compared to Google Titan. YubiKey can be used to store SSH keys, and work with OpenSSH, PuTTY, and other SSH clients to authenticate SSH servers that support protocols like U2F and FIDO2. Google Titan can also be used, but it only supports the U2F protocol for SSH authentication, limiting the SSH servers it can work with.
Common Authentication Use Cases
YubiKey keys can be used for application or service authentication in a variety of scenarios where secure and strong authentication is required. For example:
- Two-factor authentication (2FA) for web applications.
- Authentication for remote server/application access (see this post).
- Authentication for cloud services like Google Cloud, AWS, and Azure.
- Passwordless authentication, to simplify the authentication process and reduce the risk of password-based attacks like phishing and credential stuffing.
- Developer authentication for code signing.
FIDO2 Registration Flow
Here is a high-level overview of the FIDO2 registration process:
- The user initiates the registration process on their device by selecting the option to register a new security key with a particular service.
- The service (i.e. the relying party) generates a registration request, which includes a unique challenge and some information about the user and service.
- The registration request is sent to the user's device via the browser or another application.
- The user's device uses the FIDO2 client-side API to generate a new public-private key pair and create an attestation object, which includes the public key and other metadata.
- The attestation object is sent back to the service, which verifies the attestation signature and stores the user's public key.
- The service then creates a new user account (if necessary) and associates the user's public key with the account.
- The user is informed that the registration process is complete, and they can now use their security key to authenticate to the service.
Discoverable (Resident) vs Non-Discoverable Keys
FIDO2 hardware security keys can be categorised into discoverable (or resident) and non-discoverable keys. Resident keys are visible to the host operating system and can be used for purposes beyond authentication too, such as storage or encryption. Non-discoverable keys are hidden from the host and can only be used for authentication. In both cases, the private keys are generated and stored on the device itself, and cannot be copied or transferred to another device.
When you register the YubiKey security key with a service, the device generates a new public-private key pair and a credential ID, and stores the private key securely on the device. The public key and credential ID are then registered with the service. When you perform a FIDO2 authentication with a service that supports security keys, the service sends a challenge to the device, which signs the challenge using the private key stored on the device and sends the signed response back to the service. The service then verifies the signature using the public key associated with the credential ID.
Set up Your Code Development Environment
For this tutorial, we'll create Python code to interact with the YubiKey security key. Here are the things you need to have in place first:
- Set up a local development environment like VS Code or PyCharm with Python 3.x and the
pip
package manager installed. - Install the Python FIDO2 library using the command
pip install fido2
. - Have a YubiKey key available.
Once you have these components in place, we can use the FIDO2 library to check device information, register credentials, and authenticate to the device.
Get YubiKey Device Information
To get basic device information, connect the YubiKey security key to your workstation, and run the following Python script. If the security key does not support CTAP2 (e.g. Google Titan), you'll get the following error: ValueError: Device does not support CTAP2
.
# Import the necessary modules from fido2 library
from fido2.hid import CtapHidDevice
# Find the first connected FIDO2 device
dev = next(CtapHidDevice.list_devices(), None)
if not dev:
print("No FIDO2 device found.")
exit()
# Get and print device info to the console
print(f"Version: {dev.version}")
print(f"Product Name: {dev.product_name}")
print(f"Serial Number: {dev.serial_number}")
print(f"Device Version: {dev.device_version}")
You can also use the Ctap2
library for additional device information.
# Import the necessary modules from fido2 library
from fido2.ctap2 import Ctap2
from fido2.client import ClientError
from fido2.hid import CtapHidDevice
# Find the first connected FIDO2 device
dev = next(CtapHidDevice.list_devices(), None)
if not dev:
print("No FIDO2 device found.")
exit()
# Create a CTAP2 client and connect to the device
ctap2 = Ctap2(dev)
# Get and print device info to the console
try:
info = ctap2.get_info()
print(f"Authenticator Attestation GUID: {info.aaguid}")
print(f"Versions: {info.versions}")
print(f"Firmware Version: {info.firmware_version}")
print(f"Options: {info.options}")
print(f"Algorithms: {info.algorithms}")
print(f"Extensions: {info.extensions}")
print(f"Transports: {info.transports}")
print(f"Certifications: {info.certifications}")
print(f"Max Message Size: {info.max_msg_size}")
print(f"Min PIN Length: {info.min_pin_length}")
print(f"Force PIN Change: {info.force_pin_change}")
print(f"PIN UV Protocols: {info.pin_uv_protocols}")
except ClientError as e:
print(f"Error communicating with device: {e}")
Generate, Register, and Authenticate Credentials
The Python FIDO2 library provides extensive support for the generation, registration, and authentication of credentials (public-private key pairs) against a YubiKey security key. See credentials.py
file under the example
folder of the FIDO2 GitHub repository for the complete source code. The repository also contains sample source code for biometric enrolment, generation of HMAC secrets, and verifying that the credentials are signed by a designated CA.
Generally speaking, if you are developing a server-side application and need to interact with FIDO2 authenticators, you should use Fido2Client
. If you are developing a client-side application in the browser and need to interact with WebAuthn authenticators, you should use WebAuthnClient
.