Otwierasz klienta VPN, wpisujesz hasło, a sześć cyfr z aplikacji uwierzytelniającej wraca jako nieprawidłowe — dwa razy, potem trzeci. Na koncie nic się nie zmieniło od wczoraj, więc odruchem jest usunięcie wpisu w aplikacji i ponowne zeskanowanie go — zwykle to zła naprawa. Kod jednorazowy nie jest drugim hasłem: brama musi o niego poprosić w określonym kroku, z określonego zarejestrowanego wpisu, w określonym oknie czasowym, a większość odrzuceń następuje, zanim cyfry zostaną porównane. Ustal, w którym kroku jesteś, zanim tkniesz rejestrację.

Kolejność monitów mówi, w jakim przepływie jesteś

Trzy kształty obejmują prawie każde logowanie do VPN z kodem. W kształcie dwuetapowym klient wysyła hasło, serwer wydaje wyzwanie, a klient pyta ponownie. W kształcie łączonym brama chce hasła i kodu w jednym zgłoszeniu — czasem sklejonych, czasem po separatorze, który dokumentuje tylko operator. W kształcie przeglądarkowym klient nie ma żadnego pola: kod trafia na stronę internetową albo zastępuje go zatwierdzenie push, a klient desktopowy czeka na wynik.

Zły kształt wygląda jak zły kod. Wklej świeży kod w pole przeznaczone tylko na hasło, a serwer odczyta go jako część hasła. Po stronie klienta OpenVPN 2.4 i nowsze uwidaczniają różnicę: serwer odpowiadający AUTH_FAILED,CRV1: oczekuje odpowiedzi na wyzwanie, więc profil potrzebuje static-challenge lub pliku hasła w formie SCRV1:. Starszy profil z samym auth-user-pass uwierzytelnia hasło i potem się zatrzymuje, co w logu wygląda jak błąd OTP, choć żaden kod nie został wysłany.

WireGuard warto wymienić jako wyjątek: uwierzytelnia pary kluczy, a nie ludzi, więc nie ma pola na kod. Jeśli dostawca taki reklamuje, coś poza tunelem go akceptuje.

Co serwer robi z sześcioma cyframi

Kody oparte na czasie podążają za RFC 6238, który opakowuje starszą konstrukcję licznikową z RFC 4226: HMAC nad ruchomym czynnikiem, domyślnie obcięty do sześciu cyfr. W TOTP ruchomym czynnikiem jest bieżący czas Unix podzielony przez krok, 30 sekund, o ile operator go nie zmienił. Weryfikator nigdy nie porównuje twoich cyfr z zapisaną wartością; przelicza oczekiwany kod ze współdzielonego ziarna i zwykle akceptuje też sąsiednie kroki, więc wolna podróż w obie strony nie powoduje niepowodzenia logowania.

Dwa skutki są pomijane. Strefa czasowa telefonu nie ma znaczenia, bo ziarno nigdy nie widzi lokalnego zegara — liczy się tylko odchylenie od prawdziwego UTC, a urządzenie spieszące się o dwie minuty zawodzi przy każdym kodzie, pokazując czyste odliczanie. Kod jest też jednorazowy z założenia: weryfikator pamięta ostatnią wartość licznika, którą zaakceptował dla tego ziarna, więc te same cyfry wysłane ponownie są odrzucane nawet w tym samym oknie. Klient, który ponawia po przekroczeniu czasu, może spalić właśnie wysłany kod.

Tokeny licznikowe przesuwają licznik przy każdym naciśnięciu, więc wypadają z synchronizacji po wygenerowaniu kodów, których nigdy nie wysłano; zwykła naprawa to okno resynchronizacji akceptujące kilka wartości w przód. Jeśli operator zarejestrował HOTP, a twoja aplikacja pokazuje TOTP, każda wartość jest błędna w sposób, który wygląda jak problem z zegarem. URI aprowizacji za kodem QR podaje typ, cyfry, okres i hash.

Awarie, które wyglądają jak zły kod

Zarejestrowany wpis to najczęstszy winowajca. Aplikacje uwierzytelniające etykietują wpisy według wystawcy i konta, więc kto ma dwie tożsamości na tej samej VPN, może mieć dwa wpisy, których etykiety różnią się o jeden znak. Kod jest ważny dla ziarna, które go wygenerowało, i dla niczego innego. Porównaj etykietę na telefonie z nazwą użytkownika, którą zapisuje serwer, a nie z tym, co pamiętasz, że wpisałeś.

Drugi to niedopasowany zestaw parametrów. Ziarno zarejestrowane z ośmioma cyframi, okresem 60 sekund lub SHA-256 daje kody, które weryfikator oczekujący sześciu cyfr, 30 sekund i SHA-1 odrzuca, a po stronie klienta jest to nieodróżnialne od złego kodu. Pojawia się po migracjach, w których wpis dodano ponownie ze zdjęcia kodu QR albo rekord tokenu odbudowano z wartościami domyślnymi.

Trzeci nie dotyczy kodu. Bramy zwracają zwykle jeden ogólny błąd uwierzytelniania dla złego hasła, wygasłego kodu i wyłączonego konta, celowo, aby nikt nie mógł ustalić, który czynnik zawiódł. Czytaj log bramy lub RADIUS-a zamiast komunikatu klienta: wyzwanie, które nigdy nie zostało wydane, odmowa dostępu bez atrybutu OTP i odmowa polityki wyglądają na ekranie tak samo i prowadzą do różnych kolejnych kroków.

Krótka sekwencja, która to zawęża

Zacznij od najtańszego sprawdzenia i zatrzymaj się na pierwszym kroku, który nie pasuje.

  1. Poczekaj na świeży kod, wyślij go raz i zanotuj, czy wyzwanie pojawia się przed czy po zaakceptowaniu hasła.
  2. Dopasuj etykietę w aplikacji uwierzytelniającej do nazwy użytkownika, którą przechowuje serwer.
  3. Potwierdź, że profil w ogóle potrafi odpowiedzieć na wyzwanie, jak w powyższym przypadku CRV1.
  4. Porównaj zegar urządzenia ze znanym źródłem czasu; odchylenie ponad kilkadziesiąt sekund samo psuje TOTP.
  5. Zapytaj operatora, czy token to TOTP czy HOTP, i jakie cyfry, okres oraz hash zarejestrowano.

Dwie zasady nie pozwalają temu pogorszyć sytuacji. Nie ponawiaj wiele razy: wiele bram liczy niepowodzenia na konto i albo je blokuje, albo zaczyna opóźniać odpowiedzi, więc seria zamienia drobny błąd konfiguracji w zgłoszenie do pomocy. I nie rejestruj tokenu ponownie jako diagnostyki, bo to unieważnia stare ziarno; jeśli prawdziwą usterką było wyzwanie, którego klient nigdy nie wysłał, masz teraz dwie zmiany do wyjaśnienia zamiast jednej.

Trzymaj ziarno i kody odzyskiwania poza obiegiem

Sześć cyfr i kryjący się za nimi sekret nie są wymienne. Kod QR aprowizacji lub ziarno Base32 generuje każdy przyszły kod konta, więc należy do tej samej kategorii co hasło: nie w zrzucie ekranu, nie w komentarzu zgłoszenia, nie w czacie z kimś, kto pierwszy się z tobą skontaktował. Kody odzyskiwania to osobny mechanizm — jednorazowe, często całkiem pomijają pytanie o kod, a wydanie jednego na test konta usuwa drogę zapasową, której możesz potrzebować później.

Kod nie czyni też strony logowania godną zaufania: prośba o OTP na podobnej domenie zbiera ważny kod i odtwarza go w oknie czasowym, a certyfikat nadal jest tym, co mówi, gdzie wpisujesz.

Jeśli sekwencja kończy się na profilu, który nie potrafi odpowiedzieć na wyzwanie, popraw profil i przetestuj ponownie jednym świeżym kodem. Jeśli kończy się na niedopasowanych parametrach rejestracji, poproś operatora o odczytanie rekordu tokenu, zanim ktokolwiek coś zeskanuje. Jeśli kończy się na blokadzie, przeczekaj ją zamiast zmieniać klienta — przeszkodą jest stan konta, a nie aplikacja.

Powiązane artykuły