I noticed an article about the Yubikey 5C. I have the NFC version and the 'no-NFC' version - I just haven't used the NFC feature and the NFC version feels "floppy" like it's going to break when I push the button in the center to authorize. The Non-NFC version of the 5C is smaller and more rigid and the touch sensors are on the side, it's much better in my opinion. If you don't care about NFC I totally recommend the non-NFC version.
I've seen the Google Titan keys advertised. They are a bit cheaper and can use bluetooth (so everyone in your office can access your key,
kidding). However, reportedly big G had their keys manufactured in China which is not a "TAA" country, if that matters to you.
One of the more interesting features of the Yubikey 5C is PIV (Personal Identity Verification) support. You can generate a key and sign it with your CA and use it to authenticate on your web site(s). It's neat because you don't need a login, unauthenticated users wouldn't realize there's a way to "log in". Maybe an example is a CMS that gives you admin powers if you present the right PIV cert otherwise the user is seeing the public content.
There are a few pitfalls I found. The documentation for PIV on the yubico site is outdated and describes using their deprecated software and tools. Their libykpiv library doesn't seem to work, it crashes Firefox instantly. Like it just kills the browser.
But you can use opensc, pkcs11-tool, pcscd and libpcsclite-dev and these work great. You add
opensc-pkcs11.so in Firefox, go to about:preferences#privacy then scroll down to Certificates and Click Security Devices and add opensc module.
in about:config, set
security.osclientcerts.autoload = true
restart pcscd
pkill -HUP pcscd
Use openssl to create your CA. It's good to store private key stuff on a external drive that you only connect when you are using it. Just a good idea. There's the argument of using an air-gapped machine as well, which is a good idea in some cases.
I use a luks encrypted M.2 NVMe drive in a usb C enclosure. Keep the CA and private certs on that and don;t leave it connected when not in use
After you set up your CA you want to generate your private PIV key on the yubikey. Supposedly you can generate a private key off-device and import it but it didn't seem to "Stick" for me. Making the key on the device is a good idea anyway.
Then you can issue a CSR from the yubikey, sign it and import the signed cert. That works.
You need a copy of your CA public cert to put on your web server. Don't put any private keys on the web!
Then in NGINX you can set up client authentication.. it's easy in Apache too.
one thing to note, is that nginx and probably Apache "mess with the client cert" - To make it valid inside a CGI environment (which uses flat KEY=value strings), nginx prefixes each line after the first with a tab character to indicate line continuation...You have to strip off the tabs to verify the cert in PHP.
so in your nginx site config you have these extra config settings..
+ ssl_client_certificate /etc/ssl/certs/public-ca.cert.pem;
+ ssl_verify_client optional;
location ~ [^/]\.php(/|$) {
include snippets/fastcgi-php.conf;
+ fastcgi_param SSL_CLIENT_CERT $ssl_client_cert;
+ fastcgi_param SSL_CLIENT_VERIFY $ssl_client_verify;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
setting ssl_verify_client to 'optional'; means a user without a key can get to the page, if you want that. You can set it to required and it will block unauthenticated users.
Then your PHP script:
if ($_SERVER['SSL_CLIENT_VERIFY'] !== 'SUCCESS') {
echo "Client certificate verification failed.";
exit;
}
$cert = $_SERVER['SSL_CLIENT_CERT'];
// get rid of the tab characters otherwise openssl_x509_parse will fail
$cert=str_replace("\t",'',$cert);
$parsed = openssl_x509_parse($cert);
if (!$parsed) {
echo "Invalid certificate.";
exit;
}
$subject = $parsed['subject']['CN'] ?? 'Unknown';
echo "Authenticated user: " . htmlspecialchars($subject);
That only allows authenticated users (check the CN / subject for the username..) Only people who have PIV certs signed by your CA can authenticate. You can easily modify the code so it only authenticates the user if they have the correct signed PIV, otherwise they are a regular user who gets different content.
That gives you "silent" authentication without a login.
https://www.nytimes.com/wirecutter/reviews/best-security-keys/