Klucz, którego nikt nie zgadnie
Portfel kryptowalut nie przechowuje monet. Przechowuje klucz prywatny, czyli bardzo dużą liczbę. Kto ją zna, może wydać środki z portfela, więc cała ochrona sprowadza się do tego, żeby nikt inny jej nie poznał.
W bitcoinie klucz zapisuje się jako ciąg 256 bitów, czyli zer i jedynek. Gdyby wypisać wszystkie możliwe kombinacje, powstałaby lista licząca około 10⁷⁷ pozycji, czyli jedynka z 77 zerami. Tak ogromnej puli nie przeszukają nawet wszystkie komputery świata razem wzięte. Jest jednak jeden warunek: klucz musi powstać z prawdziwego losowania.
Klucza zwykle nie widać. Większość portfeli każe za to zapisać na kartce 12 albo 24 słowa, nazywane frazą odzyskiwania albo frazą seed. To kopia zapasowa: z tych słów portfel potrafi odtworzyć klucz prywatny i wszystkie kolejne klucze, które z niego wylicza. Kto zna frazę, ma więc taki sam dostęp do środków jak ten, kto zna klucz. Jak dokładnie słowa łączą się z kluczem, pokażemy pod koniec artykułu.
Warunek prawdziwego losowania zawodził zaskakująco często. W tym artykule przejdziemy przez kilkanaście lat takich wpadek, od pierwszych portfeli w przeglądarce po portfel sprzętowy z 2026 roku. Prawie wszystkie mają wspólny mianownik, więc zacznijmy od niego.
Ziarno, czyli skąd bierze się losowość
Komputer sam z siebie nie rzuca monetą. Losowe liczby wylicza generator, a punkt startowy tych wyliczeń nazywa się ziarnem. Z tego samego ziarna generator zawsze wylicza te same liczby. Dlatego o sile klucza decyduje to, ile możliwych ziaren mógł mieć generator, a nie liczba bitów w samym kluczu.
Weźmy klucz, który ma 256 bitów, ale powstał z ziarna o długości 32 bitów. Możliwych ziaren jest wtedy tylko około 4,3 mld. Dla człowieka to dużo, dla komputera niewiele: zespół bezpieczeństwa firmy Ledger oszacował, że kilka kart graficznych sprawdza wszystkie w niecałą dobę. Atakujący bierze kolejne ziarna i wylicza z nich klucze prywatne. Z każdego klucza wylicza też adres portfela, czyli jego publiczny odpowiednik numeru konta. Potem sprawdza, czy któryś adres pasuje do adresu ofiary, który każdy widzi w sieci.
Ten błąd jest starszy od bitcoina. W 2008 roku wykryto go w OpenSSL, popularnym pakiecie, którego programy używają między innymi do szyfrowania połączeń w internecie. Chodziło o wersję przygotowaną dla systemu Debian. Jeden z programistów opiekujących się pakietem usunął z kodu dwie linijki, bo program do sprawdzania pamięci zgłaszał w tym miejscu ostrzeżenia. Po tej drobnej zmianie losowanie zależało już tylko od numeru procesu, czyli liczby, którą system nadaje uruchomionemu programowi. Taki stan trwał ponad półtora roku. Na komputerach danego typu mogło w tym czasie powstać tylko około 32 tysięcy różnych kluczy każdego rodzaju.
Pierwsze portfele w przeglądarce
Kilka lat później pojawiły się portfele, które działały bezpośrednio w przeglądarce. Ich twórcy często sięgali po gotowy zestaw kodu o nazwie BitcoinJS. Za losowanie odpowiadała w nim funkcja SecureRandom() z innego pakietu, JSBN. W założeniu miała pobierać liczby z wbudowanego w przeglądarkę generatora kryptograficznego, czyli takiego, którego wyników nie da się przewidzieć. Przez błąd w kodzie program w ogóle z niego nie korzystał i nie pokazywał przy tym żadnego ostrzeżenia.
Zamiast niego działał Math.random(). To zwykły generator, dobry do gier i animacji, ale nie do kluczy. W niektórych przeglądarkach startował od ziarna wziętego z zegara. Według analizy z 2018 roku takie ziarno miało czasem „znacznie mniej niż 48 bitów” losowości.
W listopadzie 2023 roku problem szczegółowo opisała firma Unciphered i nadała mu nazwę Randstorm. Zagrożone są portfele utworzone w latach 2011–2015. Według firmy może ich być nawet miliony. Ile pieniędzy w nich zostało, Unciphered nie podaje, a szacunki innych firm bardzo się różnią.
Android i powtórzona liczba w podpisach
W sierpniu 2013 roku podobny problem dotknął użytkowników telefonów. Administratorzy serwisu bitcoin.org ostrzegli, że element Androida odpowiedzialny za bezpieczne losowanie liczb ma poważne luki. Okazało się, że systemowy generator w środowisku Java, w którym działa większość aplikacji na Androidzie, był źle uruchamiany. Właściciele portfeli na telefonach, między innymi Bitcoin Wallet i Mycelium, musieli utworzyć nowe klucze i przelać środki na nowe adresy.
Wadliwy generator zagrażał nie tylko nowym kluczom, ale też podpisom, którymi właściciel portfela zatwierdza każdą wypłatę. Atakujący wykorzystali właśnie podpisy. Przy podpisywaniu transakcji program musi za każdym razem wylosować jednorazową tajną liczbę, nazywaną nonce. Jeśli portfel użyje tej samej liczby w dwóch różnych transakcjach, w obu podpisach zostaje ten sam widoczny z zewnątrz ślad, wartość oznaczana literą r. Z dwóch takich podpisów da się prostym rachunkiem wyliczyć klucz prywatny.
Transakcje bitcoina razem z podpisami trafiają do publicznego rejestru, więc każdy może szukać takich par. Badacze z niemieckiego instytutu CISPA policzyli w 2018 roku, że przez powtórzone liczby nonce dało się ukraść łącznie 412,8 BTC. Wcześniej, w grudniu 2014 roku, ten sam błąd wystąpił w portfelu internetowym Blockchain.info. Tym razem złodziei uprzedził uczciwy badacz: przeniósł środki z zagrożonych adresów i zwrócił 267 BTC.
Z tych doświadczeń wziął się standard RFC 6979. Zgodnie z nim nonce nie jest losowany, tylko wyliczany z klucza prywatnego i treści podpisywanej transakcji. Portfel, który stosuje ten standard, przy podpisie w ogóle nie sięga po generator liczb losowych. Wadliwy generator nie może więc w tym miejscu ujawnić klucza.
Hasło zamiast losowania
Niektórzy chcieli obejść losowanie zupełnie. Tak powstały brainwallety, dosłownie „portfele w głowie”: klucz prywatny wylicza się w nich z hasła, które właściciel pamięta. Brzmi wygodnie, ale w przeciwieństwie do logowania do banku nie ma tu limitu nieudanych prób. Atakujący nigdzie się nie loguje. Wylicza klucze z kolejnych haseł na własnym komputerze, tak szybko, jak pozwala mu sprzęt.
W 2016 roku naukowcy sprawdzili w ten sposób około 300 mld haseł. Trafili na 884 używane portfele tego typu, ale prawie wszystkie ktoś już wcześniej opróżnił. Środki zostały tylko w 21 z nich. Co więcej, z połowy portfeli pieniądze znikały w ciągu 21 minut od wpłaty. Hasło wymyślone przez człowieka nie wytrzymuje takiego tempa, o czym piszemy też w artykule o polityce haseł.
Cztery miliardy możliwości
Wróćmy teraz do zbyt krótkiego ziarna, bo w ostatnich latach ta sama wpadka powtórzyła się kilka razy. Za każdym razem w roli głównej występował generator Mersenne Twister. To popularny generator do symulacji komputerowych: działa szybko, a liczby, które losuje, są równomiernie rozłożone. Nie nadaje się jednak do kryptografii. Wystarczy poznać kilkaset jego kolejnych wyników, żeby odtworzyć wewnętrzny stan generatora i przewidzieć wszystkie następne liczby. Na dodatek programiści często dają mu krótkie, zaledwie 32-bitowe ziarno. Tak było w trzech znanych przypadkach.
Pierwszy to Profanity, program do tworzenia „ładnych” adresów Ethereum, na przykład zaczynających się od wielu zer. We wrześniu 2022 roku firma 1inch pokazała, jak z takiego adresu odtworzyć klucz. Kilka dni później firma Wintermute straciła z adresu utworzonego tym programem około 160 mln dolarów.
Drugi przypadek dotyczył portfela Trust Wallet w wersji działającej jako rozszerzenie przeglądarki. Przez błąd w generatorze rozszerzenie mogło utworzyć tylko około 4 mld różnych fraz seed. Zespół Ledgera zgłosił błąd trzy dni po premierze rozszerzenia. Według niego w szczytowym momencie zagrożone było około 30 mln dolarów. Zespół przygotował tabelę wszystkich możliwych adresów, a potem odczytanie frazy dla danego adresu zajmowało mu ułamek sekundy.
Trzeci przypadek, nazwany Milk Sad, pochodzi z 2023 roku. Narzędzie Libbitcoin Explorer tworzyło frazy poleceniem bx seed, a ziarnem był bieżący czas zapisany w 32 bitach. 12 lipca 2023 roku ktoś wyprowadził z takich portfeli prawie 30 bitcoinów (29,65 BTC). Łącznie, we wszystkich kryptowalutach, zniknęło ponad 900 tys. dolarów.
Zobacz, jak wygląda to od strony atakującego. Demonstracja tworzy frazę z ziarna równego bieżącej sekundzie, a potem sprawdza kolejne sekundy, tak jak ktoś, kto wie mniej więcej, kiedy powstał portfel.
Ziarno z zegara
Uproszczony model błędu Milk Sad: generator Mersenne Twister ziarnowany bieżącym czasem w sekundach, najstarszy bajt każdego wyniku i fraza BIP39 z tych 128 bitów. Fraza wygląda na losową, ale możliwych jest tyle, ile sekund.
Portfele sprzętowe
Skoro programy na komputerach i telefonach zawodziły tak często, wielu ludzi sięga po portfele sprzętowe. To małe, osobne urządzenia, które losują frazę i trzymają klucz poza komputerem, z dala od przeglądarki i wirusów. Sam pomysł jest dobry, ale i tu zdarzały się wpadki. Każda uczy czegoś innego.
Fraza w pudełku: W 2018 roku pewien użytkownik kupił w serwisie aukcyjnym portfel sprzętowy z dołączoną kartą. Pod zdrapką była gotowa fraza seed. Kupujący wziął ją za zabezpieczenie od producenta, wpłacił środki i stracił około 34 tys. dolarów. Sprzedawca znał te słowa, bo sam je przygotował. Dlatego producent przypomina, że urządzenie z gotową frazą albo ustawionym kodem PIN trzeba od razu wyzerować, a słowa wylosować od nowa.
Wyciągnięcie ziarna: W 2020 roku zespół Kraken Security Labs wydobył z portfeli Trezor One i Model T zaszyfrowany sekret, z którego portfel odtwarza klucze. Wystarczyło około 15 minut z urządzeniem w ręku i krótkie zakłócenie jego zasilania. Sekret chronił tylko kod PIN, który badacze łatwo złamali. Przed takim atakiem chroni dodatkowe hasło do frazy seed (w standardzie BIP39 nazywane passphrase), bo urządzenie go nie przechowuje. Musi być jednak silne: słabe atakujący odgadnie u siebie.
Fraza ukryta w podpisach: W 2024 roku badacze pokazali atak nazwany Dark Skippy. Jeśli ktoś wgra do portfela złośliwe oprogramowanie, może ono przemycić frazę w liczbach nonce, które trafiają do podpisów. Frazę z 12 słów badacze odczytali już z dwóch takich podpisów. Według autorów nikt jeszcze nie użył tego ataku w praktyce. Chronią przed nim zabezpieczenia nazywane „anti-exfil”: urządzenie nie dobiera wtedy liczby do podpisu samo, tylko razem z komputerem.
Generator, który się nie włączył: W lipcu 2026 roku producent portfeli COLDCARD ujawnił, że od 2021 roku jego oprogramowanie tworzyło frazy niewłaściwym generatorem. Zamiast sprzętowego generatora losowości działał zwykły generator Yasmarang, wbudowany w środowisko MicroPython. Zawinił jeden warunek w kodzie: program sprawdzał tylko, czy ustawienie sprzętowego generatora istnieje, a nie, czy jest włączone.
Według producenta w starszych modelach, Mk2 i Mk3, atakujący ma do sprawdzenia około 2⁴⁰ możliwości, czyli nieco ponad bilion. W nowszych modelach, Mk4, Q i Mk5, możliwości jest około 2⁷². Badacze firmy Block ocenili sytuację ostrzej: według nich także w nowszych modelach może to być najwyżej 2³², czyli wspomniane wcześniej 4,3 mld.
Wgranie poprawki nie zmienia frazy, która już powstała. Środki z zagrożonych portfeli trzeba więc przenieść na nowo wylosowaną frazę. Bezpieczne są natomiast frazy, przy których użytkownik dorzucił do losowania co najmniej 50 własnych rzutów zwykłą kostką. Producent uważa je za niezagrożone tym błędem.
Ten wyjątek pokazuje, po co łączyć kilka źródeł losowości. Trezor łączy swój generator sprzętowy z losowością z komputera albo telefonu. COLDCARD pozwala dopisać wyniki rzutów kostką i przepuszcza je przez funkcję skrótu SHA-256, która z dowolnych danych robi ciąg bitów o stałej długości. Jeśli takie źródła są niezależne i dobrze połączone, fraza pozostaje bezpieczna, dopóki choć jedno z nich jest naprawdę nieprzewidywalne. Nie pomoże to jednak, gdy w samym urządzeniu działa złośliwe oprogramowanie.
Jak losowe bity zamieniają się w słowa
Wróćmy do fraz seed z początku artykułu. Jak zamienić losowość w słowa do zapisania, opisuje standard BIP39 z 2013 roku. Działa on w trzech krokach.
Najpierw portfel losuje ciąg zer i jedynek. Dla frazy z 12 słów jest to 128 bitów. Następnie przepuszcza te bity przez znaną już funkcję skrótu SHA-256 i bierze z wyniku pierwsze 4 bity. Dopisuje je na końcu jako sumę kontrolną.
Tak powstały ciąg portfel tnie na kawałki po 11 bitów. Każdy kawałek to liczba od 0 do 2047, a ta wskazuje słowo na oficjalnej liście 2048 słów. Ze 128 bitów i sumy kontrolnej wychodzi dokładnie 12 słów. Dłuższa fraza powstaje tak samo: z 256 bitów i 8 bitów sumy kontrolnej portfel układa 24 słowa.
Od bitów do słów
Suma kontrolna służy do wykrywania pomyłek. Jeśli przy przepisywaniu zamienisz jedno słowo na inne z listy, portfel zauważy to mniej więcej w 15 przypadkach na 16. Nie dodaje jednak bezpieczeństwa: fraza z 12 słów ma 128 bitów siły, a nie 132.
Oficjalnych list słów jest dziesięć, między innymi angielska, czeska i hiszpańska. Polskiej nie ma. Dokumentacja i tak odradza listy inne niż angielska, bo większość portfeli obsługuje tylko ją.
Same słowa nie są jeszcze kluczem prywatnym. Portfel przepuszcza je, w razie potrzeby razem z dodatkowym hasłem użytkownika, przez funkcję PBKDF2, która w 2048 krokach zamienia tekst w główny sekret portfela. Standard nazywa go ziarnem portfela, choć to coś innego niż ziarno generatora, o którym była mowa wcześniej. Dopiero z tego sekretu portfel wylicza kolejne klucze prywatne i adresy.
Czego uczy ta historia
Wszystkie te przypadki układają się w kilka prostych lekcji.
- Długość klucza to nie jego siła. Klucz z 256 bitów zrobiony z 32-bitowego ziarna ma tylko 32 bity siły. Tak było w Profanity, Trust Wallet i Milk Sad.
- Do kluczy służy generator kryptograficzny.
Math.random, Mersenne Twister i Yasmarang są szybkie i dobrze rozkładają liczby, ale da się je przewidzieć. - Awaria powinna zatrzymać losowanie. W portfelach przeglądarkowych i w COLDCARD błąd po cichu podmienił dobre źródło na słabe, a program działał dalej. Lepiej, żeby wtedy przestał działać.
- Kilka niezależnych źródeł jest bezpieczniejsze niż jedno. Rzuty kostką uratowały część użytkowników COLDCARD.
- Poprawka w programie nie naprawia starej frazy. Frazę z wadliwego generatora trzeba wymienić i przenieść środki.
- Człowiek nie jest dobrym źródłem losowości. Nie wymyślaj słów, nie zmieniaj ich kolejności i nie używaj frazy, którą ktoś przygotował za Ciebie.
Fraza BIP39 z naszego generatora
Nasz generator haseł ma tryb „Fraza BIP39”, zbudowany z myślą o tych lekcjach. Losowe dane pobiera z kryptograficznego generatora przeglądarki: zbiera 256 bitów i przepuszcza je przez funkcję HKDF-SHA256, która łączy dane z kilku źródeł w jeden równomierny ciąg bitów. Z tak przygotowanej puli bierze od 128 do 256 bitów, dopisuje sumę kontrolną i dobiera słowa z oficjalnej listy angielskiej. Zanim cokolwiek wylosuje, sprawdza, czy funkcje kryptograficzne przeglądarki działają tak, jak powinny, i uruchamia testy wychwytujące wybrane awarie generatora. Jeśli któryś test wykaże problem, losowanie od razu się zatrzymuje.
Gotową frazę generator sprawdza jeszcze raz, odtwarzając sumę kontrolną z samych słów. Nigdzie jej nie wysyła: w tym trybie nie sprawdzamy nawet, czy słowa pojawiły się w wyciekach haseł. Poprawność obliczeń sprawdzamy oficjalnymi wektorami testowymi BIP39, czyli wzorcowymi danymi z gotowymi wynikami. Trzeba jednak uczciwie powiedzieć, że nasze testy nie wykryją każdej złośliwej podmiany kodu w przeglądarce, na przykład przez zainfekowane rozszerzenie, i nie gwarantują bezpieczeństwa całego komputera.
Przy większych kwotach rozsądniej jest wylosować frazę na portfelu sprzętowym kupionym u producenta, najlepiej dokładając do losowania własne rzuty kostką. Jeśli korzystasz z naszego generatora, włącz tryb offline, użyj okna prywatnego, przepisz słowa na papier i zamknij stronę.
Źródła: Debian DSA-1571-1 (2008), Unciphered, Randstorm (2023), bitcoin.org, alert z 11.08.2013, M. Brengel, C. Rossow, „Identifying Key Leakage of Bitcoin Users” (RAID 2018), CoinDesk o zwrocie 267 BTC (2014), M. Vasek i in., „The Bitcoin Brain Drain” (FC 2016), 1inch o Profanity (2022), The Block o Wintermute (2022), Ledger Donjon o Trust Wallet (2023), Milk Sad (2023), Help Net Security o urządzeniu z gotową frazą (2018), Kraken Security Labs (2020), Dark Skippy (2024), Coinkite o COLDCARD (2026), Block o COLDCARD (2026), Trezor o entropii, COLDCARD o rzutach kostką, BIP39, RFC 6979.