Co było nie tak z password.es, a już nie jest

Opublikowano autor David Carrero

Ten blog ma dwanaście artykułów o tym, jak należy robić rzeczy: że długość liczy się bardziej niż symbole, że menedżer to nie wygoda, tylko jedyny sposób, by dotrzymać zasady, że procent nic nie mówi.

Napisać to jest łatwo. Niewygodnie robi się wtedy, gdy przykładasz tę samą miarę do własnej strony. Zrobiliśmy to i trzy rzeczy były nie tak. Są tu w całości, razem z liczbami, które je zdradziły, bo strona o bezpieczeństwie publikująca wyłącznie swoje sukcesy jest sporo mniej warta.

Sprawdzarka zawieszała się, kiedy pisałeś

Sprawdzarka analizuje twoje hasło za pomocą zxcvbn, biblioteki, która szuka wzorców zamiast liczyć wielkie litery. Działa bardzo dobrze i ma swoją cenę: wykonuje się w całości, naraz, na tym samym wątku, który rysuje stronę. Dopóki działa, przeglądarka nie może zrobić nic innego.

Ta cena rośnie szybko wraz z długością. Zmierzone tą samą paczką, którą serwuje strona, i hasłami, jakie ludzie naprawdę wpisują — słowa, lata, @ zamiast a:

długość jedno wykonanie
16 znaków 45 ms
20 znaków 121 ms
32 znaki 273 ms
64 znaki 843 ms
128 znaków 3621 ms

Na razie normalnie. Problemem było to, ile razy je uruchamialiśmy.

Do pola podpięte były dwa różne nasłuchy, input i keyup. Zwykły klawisz wyzwala oba. Więc przy każdej literze pełna analiza szła dwa razy: 90 ms przy haśle 16-znakowym, pół sekundy przy 32-znakowym.

I nic tego nie hamowało: ani licznik czasu, ani limit długości, ani osobny wątek. Powyżej mniej więcej dwudziestu znaków przeglądarka analizowała dłużej, niż ty naciskałeś kolejny klawisz. Uderzenia piętrzyły się w kolejce, która już nigdy się nie opróżniała, i strona przestawała odpowiadać w trakcie pisania. Wklejenie długiego hasła zawieszało ją na kilka sekund.

Naprawa to dwie małe zmiany. Usunęliśmy nasłuch keyup, bo input obejmuje już pisanie, wklejanie, kasowanie i autouzupełnianie z menedżera: połowa pracy znika, nie zmieniając absolutnie niczego z tego, co widzisz. I dodaliśmy 150 milisekund oczekiwania: dopóki piszesz, nic nie jest analizowane, a analiza rusza, gdy przestaniesz. Nikt nie pisze tak wolno, żeby to zauważyć.

Wynik jest identyczny. Barcelona1992$ nadal mówi to samo: że «barcelona» jest w słowniku, że 1992 wygląda na rok i że pada w niecałą sekundę.

Opis był ucięty w połowie zdania w piętnastu językach

Ta rzecz jest głupsza i bardziej wstydliwa.

Zdanie, które pojawia się w wyszukiwarce pod tytułem — meta description — było ucięte w piętnastu z osiemnastu języków sprawdzarki. Ocalały tylko hiszpański, koreański i japoński. Ktoś kiedyś przyciął je na długość, uciął w połowie słowa, a potem nikt nie sprawdził.

W trzech językach cięcie wypadło dokładnie w środku znaku specjalnego, więc to, co publikowaliśmy w Google, wyglądało tak:

język co serwowaliśmy co powinno być
portugalski …datas e padr&o padrões
turecki …arıyor ve ger&cced gerçekten
wietnamski …chúng tôi tra mật kh&# mật khẩu

Uderzające jest miejsce: najlepiej działająca strona w całym serwisie. Sprawdzarka konwertuje znacznie lepiej niż generator — i robiła to z zepsutym opisem. Wszystkie piętnaście jest uzupełnione.

Przy okazji w koreańskim, japońskim i wietnamskim marka przeniosła się z początku na koniec tytułu. W wynikach domena i tak widnieje osobno, więc zaczynanie od «password.es ·» marnowało tylko miejsce, które waży najwięcej.

Obciążenie jeden na sto milionów

Generator wybiera każdy znak przez crypto.getRandomValues(), dobre źródło losowości w przeglądarce. Ale żeby zamienić tę ogromną liczbę na «pozycję w alfabecie», braliśmy resztę z dzielenia, a tam czeka klasyczna pułapka: 2³² nie jest wielokrotnością 83, więc pierwsze pozycje alfabetu wypadały odrobinę częściej niż ostatnie.

Ile to «odrobinę»: przy alfabecie do 89 znaków rzędu jednej części na sto milionów. Nic to nie zmienia. Nie czyni twojego hasła mierzalnie słabszym, nawet w przybliżeniu.

Naprawiliśmy i tak, a to trzy linijki: odrzuca się 45 nadmiarowych wartości z 4 294 967 296 możliwych i losuje ponownie. Na stronie o hasłach «to pomijalne» nie jest dobrym powodem, by zostawiać obciążenie, skoro usunięcie kosztuje tyle.

Czego brakowało: żeby obietnicy pilnowała przeglądarka

Mamy cały artykuł o tym, że twoje hasło stąd nie wychodzi. To była prawda, a mimo to tylko nasze zdanie: technicznie nic nie powstrzymywało wstrzykniętego skryptu przed zrobieniem czegoś innego.

Teraz każda strona niesie politykę bezpieczeństwa treści. Dwie części, które mają znaczenie:

  • Nie uruchomi się żaden skrypt, którego nie było na stronie w chwili budowania. Każdy prawowity skrypt jest podpisany swoim odciskiem; wstrzyknięty nie pasuje i przeglądarka odmawia wykonania.
  • connect-src 'none'. Strona nie wysyła ani jednego żądania sieciowego z JavaScriptu, a teraz pilnuje tego przeglądarka. Gdyby kiedyś coś próbowało wysłać gdzieś to, co wpisujesz, nie wyszłoby.

Obietnica przestała być deklaracją i stała się regułą.

Co nadal jest nie tak

Aktualizacja z 31 sierpnia. Wszystkie trzy rzeczy z tej sekcji naprawiono tego samego dnia, w którym ukazał się ten artykuł: piętnaście języków opisuje już prawdziwy silnik, dwie skale stały się jedną, a FAQ sprawdzarki tłumaczy sprawę symboli. Zostają zapisane, bo zapis tego, co było nie tak, jest wart więcej niż zdjęcie nieskazitelnej strony.

Tu ten artykuł zarabia na swoje miejsce.

Piętnaście języków wciąż opisuje sprawdzarkę jako stary silnik. Ich tekst mówi, że odtwarza klasyczną logikę PasswordMeter. Już nie: tamten silnik wymieniono, bo Haslo1! uznawał za bardzo silne, a pięciowyrazową frazę oblewał, czyli dokładnie na odwrót. Hiszpański, koreański i japoński są zaktualizowane; pozostałe piętnaście nie.

W tym samym domu są dwie różne skale. Strona główna decyduje o sile hasła po bitach; sprawdzarka po czasie, jaki zajęłoby złamanie. Różne kryteria, i nigdzie o tym nie uprzedzamy.

Generator i sprawdzarka nie liczą tych samych symboli: jeden używa 27, drugi zakłada 33. Różnica jest mała, ale jest.

Żadna z tych trzech rzeczy nie jest poważna. Wszystkie trzy to dług i wolą być zapisane tutaj, niż czekać, aż znajdzie je ktoś inny.

Dziennik zmian tej wersji

Ponieważ repozytorium nie jest publiczne, dziennik zmian idzie tu w całości. Wersja 0.2.0, 31 sierpnia 2026:

Dodane. Polityka bezpieczeństwa treści dla każdej strony, z odciskiem każdego skryptu. Artykuł o tym, gdzie są twoje zapisane hasła, w osiemnastu językach. Kolejka redakcyjna, która decyduje, co powstaje, i która nakazuje nie publikować niczego, gdy się opróżni.

Naprawione. Opis sprawdzarki, ucięty w połowie zdania w piętnastu językach, w trzech z nich z przepołowionym znakiem. Zawieszanie karty podczas pisania. Obciążenie losowania. I brakujące zdjęcie w poprzednim artykule.

Zmienione. Tytuł sprawdzarki po koreańsku, japońsku i wietnamsku. Wdrożenie, które teraz rusza, gdy zbudowana strona już istnieje, a nie minutę wcześniej. Oraz tłumaczenia na wietnamski, hindi i arabski, które przestają blokować publikację, choć pozostają najsłabsze.


Liczby w tym artykule są zmierzone, nie oszacowane: czasy zxcvbn tą samą paczką, którą serwuje strona, i hasłami ze słowami oraz podstawieniami; obciążenie losowania policzone na 4 294 967 296 wartościach 32-bitowej liczby całkowitej. Silnikiem sprawdzarki jest zxcvbn, open source.

Zdjęcie: Ernandes Alves · Pexels

← Wróć do bloga