Ślepe podpisywanie: co naprawdę zatwierdzasz, gdy ekran nie mówi nic użytecznego

·6 min czytania·Autor: SSP Editorial Team
Okładka SSP Academy: ślepe podpisywanie i nieczytelne zatwierdzenia transakcji

Ślepe podpisywanie: co naprawdę zatwierdzasz, gdy ekran nie mówi nic użytecznego

Jest konkretny moment, w którym zdarza się większość dużych strat w kryptowalutach. Nie wtedy, gdy ktoś kradnie klucz. Wtedy, gdy ktoś patrzy na prośbę o podpis, której nie rozumie, uznaje, że pewnie jest w porządku, i naciska zatwierdź.

Branża ma na to nazwę: ślepe podpisywanie. Oznacza złożenie podpisu pod danymi, których nie da się przeczytać. I przez lata portfele traktowały to jako normalne.

Co ekran podpisu powinien robić

Obietnica bezpieczeństwa samodzielnego przechowywania opiera się na jednym założeniu: że nic się nie ruszy bez twojego zatwierdzenia. Multisig dwa-z-dwóch, portfele sprzętowe, podpisujący odcięci od sieci — każde z nich to maszyna do przekuwania twojego zamiaru w podpis.

Ta maszyneria jest warta dokładnie tyle, ile twoje zrozumienie tego, co zatwierdziłeś.

Jeśli ekran mówi „Wyślij 0,5 ETH na 0x8f3C…", twoje zatwierdzenie coś znaczy. Porównałeś je z tym, co zamierzałeś zrobić. Jeśli ekran mówi „Interakcja z kontraktem — dane: 0xa22cb465000000000000000000000000d8dA6…0001", twoje zatwierdzenie nie znaczy zupełnie nic. Nie zgadzasz się na transakcję; zgadzasz się na prostokąt szesnastkowy.

Atakujący wiedzą, który z tych dwóch ekranów mają ci pokazać.

Co kryje się w bajtach

Ładunkiem transakcji EVM są calldata: czterobajtowy selektor funkcji, po którym idą argumenty zakodowane w ABI. Doskonale czytelne dla maszyny i doskonale nieprzeniknione dla człowieka. Garstka tych selektorów to droga, którą wypływają pieniądze.

approve(address,uint256) — selektor 095ea7b3. Nadaje kontraktowi prawo do wydawania twoich tokenów. To nie przelew; to stałe upoważnienie. Podpisując je, nic nie przesuwasz — i właśnie dlatego przechodzi ono niezauważone. Opróżnienie następuje później, w harmonogramie atakującego.

setApprovalForAll(address,bool) — selektor a22cb465. Odpowiednik dla NFT, i gorszy: jeden podpis daje operatorowi władzę nad wszystkimi posiadanymi przez ciebie tokenami z tej kolekcji, teraz i w przyszłości. Nie ma pola kwoty, którym można by się pocieszyć.

increaseAllowance(address,uint256) — selektor 39509351. Dolewa do istniejącego przyzwolenia. Często przeoczany, bo nie jest tym selektorem, którego kazano wszystkim pilnować.

transferFrom(address,address,uint256) — selektor 23b872dd. Przesuwa tokeny z adresu, który już udzielił przyzwolenia. To podpis, który wreszcie wydaje to, na co pozwolił wcześniejszy approve.

Wzorzec wart przyswojenia: transakcja, która kradnie twoje pieniądze, rzadko jest transakcją, którą podpisałeś. Podpisałeś zgodę. Kradzież to osobna transakcja, wysłana później, której nigdy nie widzisz.

Dlaczego „nieograniczone" to słowo, którego trzeba szukać

Niemal każde nadużycie przyzwoleń ma jedną cechę wspólną: kwota jest praktycznie nieskończona.

Dappy proszą o nieograniczone zgody, bo tak jest wygodnie — raz zatwierdzasz i nigdy więcej nie zapytają. Skutek jest taki, że użytkownicy zostają wytresowani do rutynowego nadawania nieograniczonych praw do wydawania, a interfejs pokazujący 115792089237316195423570985008687907853269984665640564039457584007913129639935 nikomu nic nie mówi.

Jest tu subtelność, na której wykładają się naiwne implementacje. Kanoniczną wartością „nieskończonej zgody" jest dokładnie 2²⁵⁶−1, więc oczywiste sprawdzenie polega na porównaniu z tą stałą. Ale dappy rutynowo używają innych astronomicznie wielkich liczb — połowy maksimum, 0xff…f0, 10¹⁸ × 10³⁸ — które są nieograniczone w każdym praktycznym sensie, a mimo to nie przechodzą testu równości. Ostrzeżenie odpalające się tylko na dokładnej wartości wartowniczej to ostrzeżenie, które atakujący obchodzi, odejmując jeden.

Właściwy próg leży sporo poniżej wartownika i sporo powyżej czegokolwiek realnego. SSP oznacza każde przyzwolenie równe lub większe niż 2²⁵⁵ — około 5,8 × 10⁷⁶, co przekracza każdą możliwą podaż ERC-20 o dziesiątki rzędów wielkości. Nic legalnego nigdy nie zostaje błędnie oznaczone, a doszlifowanie wartości tuż poniżej maksimum nie omija ostrzeżenia.

Ten próg dotyczy wyłącznie wywołań nadających przyzwolenia. Przy zwykłym przelewie „nieograniczone" nie jest sensownym pojęciem — przesuwasz konkretną kwotę — więc tam wartownik dokładnego maksimum zostaje w spokoju.

Co robi SSP

SSP dekoduje calldata na prostą mowę na ekranie zatwierdzania. Rozpoznane selektory są pokazywane jako to, czym naprawdę są: kim jest druga strona, jaka jest kwota, czy nadawane prawa są nieograniczone. Surowy szesnastkowy nie jest już główną treścią — mieszka za sekcją Zaawansowane, dla tych, którzy go chcą.

Trzy decyzje projektowe znaczą więcej niż samo dekodowanie.

Dekoder jest wyłącznie warstwą prezentacji. Nigdy nie zmienia tego, co zostaje podpisane. Zatwierdzenie zawsze podpisuje dokładnie oryginalny ładunek; pomocnik jedynie na nowo przedstawia bajty, które wcześniej pokazywano jako surowy szesnastkowy. Dekoder zdolny zmienić ładunek byłby nową powierzchnią ataku, a nie obroną — to, co czytasz, i to, co podpisujesz, muszą być tymi samymi bajtami, zawsze.

Zawodzi, zamykając się. Nieznany selektor, zła długość, uszkodzony szesnastkowy, niestandardowe dopełnienie adresu — cokolwiek nieoczekiwanego zwraca nic, a interfejs cofa się do ogólnej akcji z surowym szesnastkowym w Zaawansowanych. Nigdy nie zgaduje. Zgadujący dekoder jest gorszy niż żaden, bo pewne siebie, ale błędne streszczenie jest groźniejsze niż widoczny szesnastkowy: szesnastkowy przynajmniej uczciwie mówi ci, że go nie rozumiesz.

Ten instynkt zamykania się sięga głębiej niż nieznane funkcje. Adres zakodowany w ABI to dwanaście zerowych bajtów, a po nich dwadzieścia bajtów adresu; słowo z czymkolwiek innym w tych początkowych bajtach nie jest zakodowane kanonicznie, a SSP traktuje je jako podejrzane, zamiast próbować je zinterpretować. Tak samo z wartościami logicznymi — przyjmowane są tylko kodowania kanoniczne: same zera (fałsz) oraz 31 zer i 0x01 (prawda). Niekanoniczne kodowania na ekranie nadającym prawa do wydawania to czerwona flaga, a nie wyzwanie dla parsera.

Symboli i miejsc dziesiętnych tokenów nigdy się nie zgaduje. Ludzka kwota pojawia się tylko wtedy, gdy token jest pewnie znany z rejestru zapisanego na urządzeniu. W przeciwnym razie dostajesz surową liczbę w jednostkach bazowych. To celowo mniej ładne: pokazanie „5,0 USDC" dla kontraktu, który jedynie nazywa siebie USDC, zamieniłoby dekoder w maszynę do kłamania, a to dokładnie ten skutek, o który chodzi atakującemu.

I jest jeszcze część strukturalna, a nie kosmetyczna. W SSP transakcja jest budowana w jednym miejscu, a zatwierdzana w innym: składana w rozszerzeniu przeglądarki, dekodowana i wyświetlana niezależnie na twoim telefonie, gdzie SSP Key przelicza skrót transakcji na urządzeniu i odmawia podpisu, jeśli nie zgadza się on z tym, co pokazano. Ślepe podpisywanie na jednym urządzeniu znaczy, że wystarczy jeden przejęty ekran. Tutaj ekran pokazujący ci zdekodowaną akcję i urządzenie trzymające drugi klucz to to samo urządzenie — a ono weryfikuje, zamiast ufać.

Co zrobić z własnymi zgodami

Traktuj approve i setApprovalForAll jako te niebezpieczne. To podpisy, które kosztują ludzi pieniądze, i wydają się nieszkodliwe właśnie dlatego, że nic się nie rusza.

Odrzucaj nieograniczone zgody, kiedy możesz. Wiele dappów przyjmie ograniczoną kwotę, jeśli ją ustawisz. To tarcie, i ogranicza twoją stratę do tego, co naprawdę zamierzałeś wydać.

Przejrzyj to, czego już udzieliłeś. Stare zgody nie wygasają. Przyzwolenie dane protokołowi dwa lata temu wciąż żyje, a jeśli ten kontrakt zostanie później przejęty, jest otwartą drogą do twoich tokenów. Cofanie zgód idzie szybko i jest najbardziej opłacalną godziną porządków w samodzielnym przechowywaniu.

Kiedy ekran nic ci nie mówi, to właśnie jest sygnał. Jeśli twój portfel nie potrafi powiedzieć, co robi transakcja, to jest informacja — a nie niedogodność do przeklikania. Właściwą odpowiedzią na nieczytelną prośbę jest zatrzymanie się, a nie mrużenie oczu.

Celem nigdy nie było zmuszenie cię do czytania szesnastkowego. Celem jest to, żeby w chwili, gdy coś zatwierdzasz, ty i twój portfel zgadzali się co do tego, co to było.

Udostępnij ten artykuł

Powiązane artykuły