Znaczniki czasu Unix w zabezpieczeniach i uwierzytelnianiu — Wygaśnięcie tokenów, sesje i ograniczanie częstotliwości
Czas to podstawa bezpieczeństwa. Wygaśnięcie tokenów, limity czasu sesji, okna ograniczania częstotliwości żądań oraz zapobieganie atakom powtórzeń wszystko zależy od dokładnego pomiaru czasu — a we współczesnych systemach ten pomiar prawie zawsze opiera się na znacznikach czasu Unix.
Zrozumienie, jak znaczniki czasu Unix działają w kontekście bezpieczeństwa, pomaga w debugowaniu błędów uwierzytelniania, prawidłowym projektowaniu systemów sesji i unikaniu powszechnych luk powstałych z błędów w obsłudze znaczników czasu.
Użyj Konwertera znaczników czasu Unix, aby przeglądać konkretne wartości znaczników czasu i konwertować je na daty czytelne dla człowieka.
Co czyni znaczniki czasu Unix dobrymi dla bezpieczeństwa
Znacznik czasu Unix to jedna liczba całkowita reprezentująca sekundy, które upłynęły od 1 stycznia 1970 UTC. Ta prosta reprezentacja ma kilka właściwości istotnych dla bezpieczeństwa:
Niezależny od strefy czasowej. Znacznik czasu 1720000000 oznacza ten sam moment na całej Ziemi, niezależnie od lokalnej strefy czasowej. Sformatowany ciąg daty i godziny taki jak „2024-07-03 14:00:00" jest niejednoznaczny — 14:00 w której strefie czasowej? Znaczniki czasu Unix całkowicie eliminują tę niejednoznaczność. Kontrole bezpieczeństwa, które porównują znaczniki czasu między systemami w różnych strefach czasowych, nie mogą się rozchodzić ze względu na różnice w interpretacji strefy czasowej.
Łatwa arytmetyka. Sprawdzenie, czy token wydany w czasie iat wygasł w oknie 1 godziny to: current_timestamp > iat + 3600. Żadnej matematyki kalendarza, żadnego parsowania daty i godziny, żadnych dostosowań do czasu letniego. Arytmetyka jest dokładna i szybka.
Kompaktowe przechowywanie. Liczba całkowita 32-bitowa lub 64-bitowa jest znacznie mniejsza niż sformatowany ciąg daty i godziny. Dla systemów, które generują i przechowują miliony tokenów, sesji lub wpisów dziennika, to ma znaczenie.
Wygaśnięcie tokenów JWT: roszczenia iat, exp i nbf
Tokeny JSON Web Token (JWT) używają znaczników czasu Unix dla swoich roszczeń związanych z czasem:
iat(issued at): Znacznik czasu Unix, gdy token został utworzony. Używany do pomiaru wieku tokenu i wykrycia tokenów utworzonych dawno temu.exp(expiration time): Znacznik czasu Unix, po którym token nie powinien być zaakceptowany. Serwer walidujący sprawdzacurrent_time > exp— jeśli prawda, token wygasł i jest odrzucany.nbf(not before): Znacznik czasu Unix, przed którym token nie powinien być zaakceptowany. Mniej powszechnie używane, ale przydatne dla tokenów wygenerowanych z wyprzedzeniem, które powinny stać się ważne dopiero w określonym przyszłym momencie.
Typowy ładunek JWT wygląda następująco:
{
"sub": "user_12345",
"iat": 1720000000,
"exp": 1720086400,
"nbf": 1720000000
}
W tym przykładzie exp - iat = 86400, co oznacza, że token jest ważny przez dokładnie 24 godziny (86 400 sekund).
Częsty błąd: ustawienie wygaśnięcia w milisekundach. Ponieważ Date.now() w JavaScripcie zwraca milisekundy, programiści czasami obliczają exp jako Date.now() + 86400000 (dodając milisekundy do znacznika czasu w milisekundach). Jeśli biblioteka JWT oczekuje sekund, tworzy to token z wygaśnięciem za 1000 lat zamiast za 24 godziny. Zawsze dziel przez 1000 podczas pracy w JavaScripcie: Math.floor(Date.now() / 1000) + 86400.
Aby sprawdzić znacznik czasu wygaśnięcia JWT: zdekoduj ładunek base64 i sprawdź exp. Wklej znacznik czasu do Konwertera znaczników czasu Unix, aby zobaczyć czytelny dla człowieka czas wygaśnięcia.
Limity czasu sesji i okna przesuwne
Sesje aplikacji webowej używają znaczników czasu do implementacji dwóch typów limitów czasu:
Limit czasu bezwzględny: Sesja wygasa w ustalonym czasie po utworzeniu, niezależnie od aktywności. Implementacja: przechowuj znacznik czasu Unix created_at podczas tworzenia sesji. W każdym żądaniu sprawdź current_time - created_at > max_session_seconds. Jeśli prawda, unieważnij sesję i wymagaj ponownego zalogowania.
Limit czasu przesuwny (timeout bezczynności): Sesja wygasa po okresie bezczynności. Każde żądanie przedłuża okno. Implementacja: przechowuj znacznik czasu Unix last_active, aktualizowany w każdym żądaniu. Sprawdź current_time - last_active > idle_timeout_seconds. Jeśli prawda, sesja była bezczynna zbyt długo i wygasła.
Większość aplikacji potrzebuje obu: okna przesuwnego dla wygody (brak wylogowania podczas aktywnego użytku) i absolutnego maksimum (żadna sesja nie pozostaje ważna na nieskończoność nawet przy ciągłej aktywności). Typowa kombinacja: 30-minutowy timeout bezczynności, 8-godzinowe absolutne maksimum.
Implementacja: `python if current_time - session["last_active"] > 1800: # 30 min idle invalidate_session() if current_time - session["created_at"] > 28800: # 8 hr absolute invalidate_session() session["last_active"] = current_time `
Okna ograniczania częstotliwości
Ograniczanie częstotliwości zwykle działa w stałych oknach czasowych mierzonych w sekundach. Powszechne implementacje:
Stałe okno: Dopuszczaj N żądań na okno. Okno to okres taki jak „na minutę" lub „na godzinę", zdefiniowany jako zakres znaczników czasu Unix. Okno dla bieżącej minuty to floor(current_time / 60) * 60 do tej wartości plus 60. Gdy żądanie przybywa, zwiększ licznik przyporządkowany bieżącemu znacznikowi czasu okna. Jeśli licznik > N, odrzuć.
Okno przesuwne: Dokładniejsze niż okno stałe. Śledź znacznik czasu każdego żądania w ciągu ostatnich N sekund. W każdym nowym żądaniu policz, ile przechowywanych znaczników czasu mieści się w zakresie current_time - window_seconds do current_time. Jeśli liczba >= limit, odrzuć; w przeciwnym razie zapisz nowy znacznik czasu.
Okno przesuwne zapobiega problemowi na granicy okien stałych (użytkownik mógłby dokonać N żądań o 11:59 i N żądań o 12:00, otrzymując 2N żądań w ciągu dwóch kolejnych minut).
Dla systemów rozproszonych z wieloma serwerami ograniczanie częstotliwości wymaga wspólnego magazynu (Redis jest powszechny). Licznik lub lista znaczników czasu jest przechowywana z TTL odpowiadającym oknu, więc wygasa automatycznie. Klucz to zwykle ratelimit:{user_id}:{window_start_timestamp}.
Zapobieganie atakom powtórzeń za pomocą znaczników czasu
Atak powtórzenia to sytuacja, gdy atakujący przechwytuje ważne żądanie i przesyła je ponownie, aby spowodować powtórzenie tej samej akcji. Znaczniki czasu służą do utrudnienia tego.
Nonce oparte na znaczniku czasu: Dołącz bieżący znacznik czasu Unix do podpisu żądania. Serwer odbierający sprawdza, czy znacznik czasu mieści się w akceptowalnym oknie (np. ±5 minut od bieżącego czasu serwera). Żądania ze znacznikami czasu poza oknem są odrzucane, nawet jeśli podpis jest ważny. To ogranicza okno powtórzenia do 5 minut — nie idealne, ale zapobiega nieskończonym atakom powtórzeń.
Ograniczenie: 5-minutowe okno nadal pozwala na pewne powtórzenia. Aby całkowicie wyeliminować powtórzenia, musisz także przechowywać i sprawdzać, czy konkretna kombinacja znacznika czasu+nonce nie została wcześniej użyta (zwykle w krótkotrwałej pamięci podręcznej z TTL równym oknu powtórzenia).
TOTP (Time-based One-Time Passwords): Aplikacje takie jak Google Authenticator używają znaczników czasu Unix do generowania kodów opartych na czasie. Bieżące okno 30 sekund (floor(current_time / 30)) jest używane jako wejście do funkcji HMAC wraz ze wspólnym sekretnym kluczem. Kod zmienia się co 30 sekund, co uniemożliwia ataki powtórzeń poza tym oknem.
Pochylenie zegara i synchronizacja czasu
Bezpieczeństwo oparte na znacznikach czasu Unix działa tylko, jeśli zegary są zsynchronizowane. Jeśli zegar klienta jest 10 minut przed zegarem serwera, klient generuje JWT exp, który serwer uważa już za należący do przyszłości — lub token z nbf, który jeszcze nie stał się ważny na kliencie.
W praktyce:
- Serwery powinny używać NTP (Network Time Protocol) do utrzymania synchronizacji zegarów w obrębie milisekund
- Walidatory JWT powinny dopuszczać małe tolerancje pochylenia zegara (1–5 minut) podczas sprawdzania
expinbf - Nie ufaj znacznikom czasu dostarczonym przez klienta w decyzjach bezpieczeństwa — zawsze używaj znaczników czasu po stronie serwera dla
iat,expi rekordów audytu
Konwerter znaczników czasu Unix pokazuje bieżący znacznik czasu widziany przez twoją przeglądarkę. Jeśli debugujesz niezgodność, porównaj to z date +%s na serwerze, aby sprawdzić dryfowanie zegara.
Podpisy HMAC i żądania z kluczem czasowym
Wiele systemów webhook i schematów uwierzytelniania API (AWS Signature Version 4, weryfikacja webhook Stripe, webhook Shopify) dołącza bieżący znacznik czasu do podpisanej zawartości. Podpis obejmuje zarówno ładunek jak i znacznik czasu, a odbiornik waliduje:
1. Podpis pasuje (ładunek + znacznik czasu nie zostały zmienione) 2. Znacznik czasu mieści się w akceptowalnym oknie (żądanie nie jest przestarzałe ani powtórzeniem)
AWS SigV4 używa formatu daty (YYYYMMDDTHHMMSSZ) zamiast znacznika czasu Unix, ale logika bazowa jest taka sama — czas jest częścią tego, co jest podpisane, i odbiornik odrzuca żądania ze znacznikami czasu poza 15-minutowym oknem.
Podczas debugowania błędów podpisu webhook jedną wspólną przyczyną jest przestarzały znacznik czasu — zegar nadawcy webhook jest nieakuralny lub istnieje znaczne opóźnienie w dostarczeniu (kolejki komunikatów, ponowne próby), które przesuwa znacznik czasu poza akceptowalne okno.
Debugowanie błędów uwierzytelniania związanych ze znacznikami czasu
Najczęstsze błędy uwierzytelniania związane ze znacznikami czasu:
1. Token wygasł: Sprawdź exp przez zdekodowanie JWT i konwersję exp na datę. Konwerter znaczników czasu Unix robi to w jednym kroku. 2. Token jeszcze nie jest ważny: nbf jest w przyszłości. Sprawdź, czy token został wygenerowany z przyszłą datą rozpoczęcia. 3. Pochylenie zegara: Zegary serwera i klienta są niezgodne o więcej niż okno tolerancji. Sprawdź oba zegary względem referencji. 4. Milisekundy vs sekundy: Znacznik czasu 13-cyfrowy przekazany tam, gdzie oczekiwany jest 10-cyfrowy, lub odwrotnie. Token z exp: 1720086400000 (milisekundy) nigdy nie wygaśnie — reprezentuje datę za 56 000 lat. 5. Zamieszanie w strefach czasowych w dziennikach: Wpisy dziennika pokazują znacznik czasu, który wydaje się nieakuralny, ponieważ dzienniki używają czasu lokalnego, ale porównanie znaczników czasu używa UTC. Znaczniki czasu Unix są zawsze UTC; sformatuj je jawnie podczas wyświetlania.


