Klucz, który nigdy nie opuszcza urządzenia

Klucz SSH przechowywany w ~/.ssh/id_ed25519 to plik o rozmiarze 411 bajtów. Bez hasła jest gotowy do użycia: skopiowanie tego pliku daje pełen dostęp do wszystkich serwerów, na których zarejestrowano odpowiadający klucz publiczny.

Ustawienie hasła na kluczu (-N "...") poprawia sytuację tylko częściowo. Na skompromitowanej maszynie keylogger przechwyci hasło podczas podawania. Kopie zapasowe w chmurze powielają problem, zamiast go eliminować.

YubiKey rozwiązuje to inaczej: klucza prywatnego nie ma w żadnym pliku. Żyje wewnątrz układu scalonego secure element, który wykonuje operacje kryptograficzne wewnętrznie i zwraca tylko wynik (podpis, odszyfrowany tekst, potwierdzenie autentykacji). Skradziony YubiKey bez znajomości PIN-u nie umożliwia złożenia żadnego podpisu, a po kilku błędnych próbach chip się blokuje.

To pierwszy post z serii o YubiKey.
Teraz: Wstęp, pokazanie co możesz zyskać i jakie pojęcia musisz znać oraz kiedy taki klucz ma sens.
W kolejnych odcinkach: modele, instalacja narzędzi, SSH, podpisywanie commitów, szyfrowanie sekretów w GIT.

Architektura urządzenia

YubiKey 5 to nie jest jeden klucz. To kilka niezależnych appletów (aplikacji na chipie), z których każda realizuje inny standard. Każda ma własną pamięć, własny PIN i własny protokół komunikacji. Dokładnie tak samo, jak na karcie kredytowej lub karcie SIM. Wszystkie applety są wzajemnie izolowane, działają jednocześnie i mają niezależną konfigurację i pamięć.

flowchart TB
    YK([YubiKey 5])
    YK --> F[FIDO2 / WebAuthn]
    YK --> P[OpenPGP card]
    YK --> V[PIV smart card]
    YK --> O[OATH]
    YK --> T[OTP / Challenge-Response]

    F -->|"passkeys, ed25519-sk"| FU[SSH FIDO2, logowanie www]
    P -->|"RSA, ed25519, cv25519"| PU[GPG sign/encrypt, SSH przez gpg-agent, podpisy commitów]
    V -->|"X.509, klucze prywatne w slotach"| VU[Smart card login, age-plugin-yubikey]
    O -->|"TOTP, HOTP, do 32 kont"| OU[2FA: GitHub, Google, AWS]
    T -->|"HMAC-SHA1, static password"| TU[LUKS unlock, KeePassXC, fallback]

Słownik pojęć

Standardy uwierzytelniania webowego

FIDO U2F (Universal 2nd Factor, ~2014) — pierwszy szeroko wdrożony standard sprzętowych kluczy. Służy wyłącznie jako drugi faktor, po haśle. Przepływ logowania wygląda następująco: po podaniu loginu i hasła strona wyświetla komunikat "dotknij swojego klucza bezpieczeństwa", użytkownik wkłada YubiKey do USB i dotyka metalowego dysku. Nie ma kodu do przepisywania, SMS-a ani cyfr z aplikacji mobilnej: po dotknięciu strona przeładowuje się z aktywną sesją.

Pod spodem przeglądarka komunikuje się z kluczem binarnie po protokole HID (USB-HID, NFC albo Bluetooth). Wysyła do chipa losowe wyzwanie (challenge) razem z identyfikatorem strony, chip podpisuje je kluczem prywatnym i zwraca podpis do przeglądarki. Użytkownik nie widzi żadnego ciągu znaków. Każde logowanie ma inny challenge, więc atak powtórzeniowy (replay attack) jest niemożliwy.

Klucz publiczny rejestruje się w serwisie raz, klucz prywatny zostaje na urządzeniu na zawsze.

Protokół jest wciąż obsługiwany przez YubiKey ze względu na wsteczną kompatybilność, ale wszystkie nowe wdrożenia używają FIDO2.

FIDO2 / WebAuthn (2019) — następca U2F. Połączenie standardu po stronie urządzenia (CTAP2: protokół klient ↔ klucz) i przeglądarkowego API (WebAuthn: JS-owe API rejestracji i logowania). Kluczowa różnica: hasło nie jest wymagane. Przepływ logowania ogranicza się do podania loginu (przeglądarka może sama wykryć dostępne passkeye), dotknięcia YubiKey i wpisania PIN-u raz na sesję. Zamiast dwóch kroków (hasło + klucz) wystarczy jeden gest. Trzy istotne zmiany względem U2F:

  1. Brak hasła: FIDO2 może być jedynym czynnikiem (passwordless), nie tylko drugim.
  2. PIN: w U2F samo dotknięcie wystarczyło; w FIDO2 chip wymaga PIN-u wpisywanego raz na podpięcie klucza, co czyni skradziony YubiKey bezużytecznym.
  3. Odporność na phishing: chip przechowuje domenę, dla której klucz został zarejestrowany, i odmówi podpisu dla g00gle.com nawet gdy strona wygląda identycznie.

Mechanizm passwordless to podstawa oferty passkey w produktach Microsoftu, Google i Apple.

Passkey — termin wprowadzony przez FIDO Alliance (promowany przez Apple/Google/Microsoft) na discoverable credential FIDO2 z ergonomicznym UX. Technicznie to ten sam standard, z rozróżnieniem na synced passkeys (synchronizowane między urządzeniami, np. iCloud Keychain) i device-bound passkeys (przywiązane do jednego urządzenia, np. YubiKey). Z perspektywy użytkownika passkey zastępuje hasło uwierzytelnieniem odciskiem palca, Face ID lub PIN-em. Passkey może być przechowywany na YubiKey, w iCloud Keychain, w Google Password Manager lub synchronizowany między tymi miejscami. Zalety: eliminacja haseł, odporność na phishing, możliwość działania offline. Ograniczenie: synced passkey (np. w iCloud Keychain) uzależnia bezpieczeństwo od bezpieczeństwa konta chmurowego (Apple ID) i nie jest już device-bound. Gwarancję, że klucz prywatny nigdy nie opuści chipa, daje wyłącznie device-bound passkey na YubiKey.

Resident Key (Discoverable Credential) — klucz FIDO2 przechowywany w całości na YubiKey. "Zwykły" (non-resident) klucz FIDO2 wymaga pliku handle na dysku komputera, bo YubiKey trzyma tylko seed do wyprowadzenia klucza. Praktyczna różnica: z resident key można wpiąć YubiKey w dowolny komputer (obcy laptop, kiosk, świeżo zainstalowany system), zalogować się do GitHuba lub wykonać ssh-keygen -K i uzyskać dostęp do zarejestrowanych kont. Bez resident key konieczny jest plik-handle z poprzedniej maszyny. Resident key zapewnia pełną mobilność, ale liczba slotów jest ograniczona (25–100, zależnie od wersji firmware), dlatego powinien być zarezerwowany dla kluczowych serwisów.

Algorytmy podpisu

Wszystko, co YubiKey robi w tej sekcji, opiera się na kryptografii asymetrycznej: para kluczy składa się z prywatnego (tajny, przechowywany na chipie) i publicznego (jawny, rejestrowany na serwerze lub w authorized_keys). Cokolwiek zostanie podpisane kluczem prywatnym, każdy może zweryfikować kluczem publicznym; cokolwiek zaszyfrowane kluczem publicznym, odszyfruje wyłącznie właściciel klucza prywatnego. Matematycznie można to zrealizować na kilka sposobów, każdy z innym balansem szybkości, długości klucza i odporności na ataki. Poniżej cztery algorytmy, które pojawiają się przy konfiguracji YubiKey: od najnowszego i preferowanego po starsze warianty zachowane dla kompatybilności.

ed25519 — algorytm podpisu cyfrowego oparty na krzywej eliptycznej Curve25519. Szybki, bezpieczny, ma krótkie klucze (32 bajty na klucz publiczny). W kontekście SSH typ klucza nazywa się ssh-ed25519 — preferowany wybór dla wszystkich nowych kluczy.

ed25519-sk — wariant ed25519 dla SSH, gdzie operacja podpisu jest delegowana do zewnętrznego security key (-sk). Klucz prywatny nigdy nie istnieje na komputerze; SSH pyta urządzenie FIDO2 o podpis przez bibliotekę libfido2.

RSA-4096 — klasyczny algorytm w GPG. Wciąż bezpieczny, ale klucze są długie (~3 kB na publiczny), operacje wolniejsze. Dla nowych konfiguracji GPG na YubiKey 5 wybierz raczej ed25519.

Smart card / OpenPGP

OpenPGP — standard szyfrowania i podpisywania. YubiKey realizuje OpenPGP card specification — chip zachowuje się jak fizyczna karta procesorowa dla GPG. Trzyma do trzech subkluczy: Signature, Encryption, Authentication (może służyć jako klucz SSH przez gpg-agent). Daemon scdaemon jest mostem między GnuPG a fizycznym chipem.

PIV (Personal Identity Verification) — standard NIST SP 800-73, pierwotnie dla amerykańskich agencji rządowych. Definiuje cztery sloty na certyfikaty X.509 (9a Authentication, 9c Signature, 9d Key Management, 9e Card Authentication). YubiKey implementuje PIV i można pod niego podpiąć biblioteki PKCS#11 — w praktyce: smart card login do Windows, autoryzacja VPN, age-plugin-yubikey do szyfrowania sekretów.

Hasła jednorazowe i czasowe

OTP (One-Time Password) — w nomenklaturze Yubico to konkretnie Yubico OTP, własnościowy protokół. Po dotknięciu klucza emulowana klawiatura wpisuje 44-znakowy ciąg w stylu ccccccvrlhknbujfgdeelrid.... Walidacja odbywa się przez serwery Yubico. W praktyce funkcja ta rzadko jest potrzebna (w kolejnych częściach serii zostanie wyłączona); jedyną jej zaletą jest działanie od razu po wyjęciu z pudełka.

TOTP (Time-based OTP, RFC 6238) — 6-cyfrowy kod regenerowany co 30 sekund, taki sam mechanizm jak w Google Authenticatorze. YubiKey ma do 32 slotów na seedy TOTP w applecie OATH. Kody generuje chip, seed nigdy nie opuszcza urządzenia. Do wyświetlania kodów wymagana jest aplikacja Yubico Authenticator, ponieważ YubiKey nie ma ekranu.

HOTP (HMAC-based OTP, RFC 4226) — wariant OTP z licznikiem zamiast czasu. Rzadziej używany.

Static Password — YubiKey może emitować stały ciąg znaków przy dotknięciu. System widzi to, jak wpisanie hasła z klawiatury. Nie jest kryptograficznie bezpieczny, ale lepszy niż 40-znakowe hasło zapisywane na kartce.

Challenge-Response — YubiKey przyjmuje na wejściu challenge (ciąg bajtów), przetwarza algorytmem HMAC-SHA1 z zapisanym sekretem i zwraca response. Używane przez yubikey-luks i KeePassXC.

Jak to działa krok po kroku

Przykładowy przepływ: logowanie do GitHuba przy użyciu passkey na YubiKey.

  1. Otwarcie github.com/login i kliknięcie "Sign in with passkey".
  2. Serwer GitHuba generuje losowe wyzwanie (challenge): 32 bajty losowych danych wysyłane do przeglądarki.
  3. Przeglądarka prosi YubiKey o podpis. Wyświetlany jest komunikat "dotknij klucza", opcjonalnie z prośbą o PIN (raz na sesję).
  4. YubiKey weryfikuje, czy posiada resident key dla github.com. Jeśli tak, podpisuje challenge kluczem prywatnym wewnątrz chipa. Klucz prywatny nigdy nie opuszcza urządzenia.
  5. Podpisany challenge trafia do przeglądarki, a stamtąd na serwer GitHuba.
  6. Serwer weryfikuje podpis kluczem publicznym zapisanym na koncie. Po pomyślnej weryfikacji ustawia ciasteczko sesji i przyznaje dostęp.
sequenceDiagram
    participant U as Użytkownik + YubiKey
    participant S as github.com

    S->>U: challenge
    U->>S: podpisany challenge
    S->>U: sesja aktywna
  1. Challenge jest losowy: każda sesja ma inny. Atak powtórzeniowy (replay attack) jest niemożliwy.
  2. YubiKey weryfikuje domenę żądania. Phishing na g1thub.com nie zadziała: chip zna prawdziwą domenę z momentu rejestracji i odmówi podpisu dla innej.
  3. Klucz prywatny nie opuszcza chipa. Serwer otrzymuje tylko podpis i potwierdza obecność klucza prywatnego, nie jego wartość.
  4. PIN i dotyk są lokalne. Nie są nigdzie przesyłane i nie mogą zostać przechwycone przez sieć.

Ten sam wzorzec (challenge, podpis na chipie, weryfikacja kluczem publicznym) działa w SSH, w git push przez SSH oraz w sudo z PAM-U2F. Różnice dotyczą wyłącznie warstwy transportu.

Kiedy to ma sens

1. Praca z wielu maszyn: laptop, desktop, serwery

Klasyczny problem: klucz SSH przechowywany w pliku wymaga albo kopiowania między maszynami (ryzyko wycieku), albo utrzymywania osobnego klucza na każdej (rozrost authorized_keys). YubiKey eliminuje oba warianty.

  • Klucz prywatny nigdy nie istnieje na dysku — nie ma czego kopiować.
  • Podłączenie YubiKey w dowolnym komputerze i wykonanie ssh-keygen -K daje dostęp do handle'y. Działa na laptopie, desktopie, obcym sprzęcie awaryjnym, w biurze.
  • Po odłączeniu klucza nie zostaje żaden ślad na maszynie.
  • Skradziony laptop bez fizycznego klucza i PIN-u nie daje dostępu do żadnego serwera.

2. Podpisywane commity i SSH do GitHuba

Verified commits + bezpieczne SSH:

  • Klucz GPG na YubiKey podpisuje commity. Zielona plakietka Verified w GitHubie.
  • Ten sam klucz GPG (subkey Authentication) służy jako klucz SSH do git push.
  • TOTP do logowania na GitHub/GitLab — w applecie OATH na tym samym kluczu.
  • Jedno urządzenie zastępuje: klucz SSH + Google Authenticator + GPG sign.

3. Zespół z polityką bezpieczeństwa

Organizacja wymaga kryptograficznej gwarancji, że sesję otworzył człowiek z fizycznym kluczem, a nie skrypt operujący wykradzionym plikiem:

  • sshd_config ogranicza PubkeyAcceptedAlgorithms tylko do sk-ssh-ed25519@openssh.com.
  • W authorized_keys każdy klucz oznaczony verify-required: wymuszony PIN + touch.
  • Brak fallbacku do haseł.
  • Wyciek klucza prywatnego niemożliwy, bo on nie istnieje poza chipem.