Неочевидная опасность холодных кошельков
0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53
Что за строка выше? Не знаете? И я не знаю. А там — отправка одного эфира на сторонний адрес. Представьте, как неприятно было бы узнать о том, что подписал такое постфактум.
А владельцам базовых Ledger Nano S, Trezor Model T, SafePal S1 и ещё кучи кошельков и представлять не нужно. Для них подобное — реальность.
Ситуация абсурдна. Человек покупает хардверный кошелёк, который весь такой безопасный, пределы которого никогда не покидает сид-фраза, чтобы в итоге в какой-то момент потерять деньги просто потому, что у человека нет уверенности, что он подписывает.
Почему так?
Когда мы хотим осуществить какое-то web3-взаимодействие, мы формируем тело транзакции вида
и формируем hash-to-sign
который и даёт нам ту самую таинственную строку из начала поста. И с точки зрения блокчейна тут нет проблем: сеть знает, от кого, кому, сколько и чего перевести. Проблема в том, что мы не знаем.
Чтобы мы знали, горячие (то есть подключённые к интернету) кошельки научились симулировать транзакции. Они могут это делать, т.к.:
- имеют RPC-доступ; - знают текущий state;
- могут делать десятки eth_call - могут подтягивать ABI и цены;
- могут ошибаться и обновляться.
Что из этого может делать холодный кошелёк? Очевидно, ничего — иначе какой из него, собственно, холодный кошелёк.
Таким образом, source of truth для холодных кошельков всегда выступает их «горячая» часть: приложение или расширение, которое по своей специфике всегда может быть скомпрометировано.
То есть злоумышленнику достаточно подменить строку, передаваемую на ваш холодный кошелёк, не трогая передаваемую метадату — и вуаля. Вы думаете, что подписываете одну транзакцию, а по факту — совсем другую.
Чтобы этого избежать, производители холодных кошельков создают разного рода реестры, подписывают метадату и транзакцию в единый payload и применяют ещё много разных, по уровню эффективности, методов.
Типичная стратегия — зашивать знания о популярных токенах и их параметрах в прошивку, а затем бесконечно её обновлять.
Это ведёт к:
- раздутой прошивке (особенно в DeFi-богатых сетях);
- постоянной зависимости от обновлений (что если пользователь не обновил? что если supply-chain атака в обновлении?);
- скрытому сбору телеметрии (нужно знать, какие токены есть у пользователя, чтобы «оптимально» зашивать их в прошивку).
Также появляются более сложные решения:
- Ledger Transaction Check: перед подписью на Ledger Flex/Stax отправляет unsigned tx провайдерам (Blockaid, Tenderly, Cyvers) для симуляции; устройство показывает риски, изменения балансов и тип tx, проверяя криптопривязку.
- Tangem VTX: симулирует tx с Blockaid, бандлит tx + симуляцию + отчёт в signed payload; карта/приложение показывает читаемые эффекты (токены, контракты), предотвращая tamper.: перед подписью на Ledger Flex/Stax отправляет unsigned tx провайдерам (Blockaid, Tenderly, Cyvers) для симуляции; устройство показывает риски, изменения балансов и тип tx, проверяя криптопривязку.
- Liminal Blind Signing Protection: для EVM в cold multisig отображает readable-данные (адрес, amount, контракты) на экране Ledger с cross-verification хэшей.
Но достаточно пары минут, чтобы представить массу вариантов, как это может быть скомпрометировано:
- Неверифицированный контракт. Нет публичного ABI — нет проверки.
- Заведомо неправильный адрес контракта или кошелька.
- Если токена нет в реестре, мы ничего не знаем о его разрядности. Отправку скольких токенов мы только что подписали? 2? 20? 200?
- Мы полагаемся на централизованную инфраструктуру. Один удачный троян на сервере — и все транзакции стали «безопасными».
Как нетрудно увидеть, подход, с которым к вопросу читаемости подписываемого подходят компании-производители кошельков, мало что решает, зато добавляет серию дополнительных векторов атаки.
Но я не хотел бы бросать камни в производителей железа — они делают что могут.
Проблема в том, что во многих сетях (особенно EVM) нет механизма, который позволял бы блокчейну проверять, какие именно допущения (метаданные) использовал аппаратный кошелёк, когда показывал транзакцию человеку.
В результате горячий кошелёк может подсунуть сайнеру любую «интерпретацию», а блокчейн никак не отличит транзакцию, показанную честно, от транзакции, показанной с подменёнными параметрами (например, неправильное число знаков после запятой или «левый» метод контракта).
Критикуешь — предлагай.
Окей.
В экосистеме Polkadot для каждой цепи есть формализованный словарь метаданных, описывающий типы транзакций, их поля, типы данных, параметры активов (включая decimals, идентификаторы ассетов и т.д.).
Из этого словаря собирается Merkle-дерево, а его root-хэш включается в рантайм и становится частью состояния/логики валидации: все узлы сети согласны, что актуальная метадата — именно эта.
Горячий кошелёк, формируя транзакцию для Polkadot-подобной сети, помимо самих байтов транзакции даёт сайнеру меркловские ветки от того самого root-хэша для нужных кусочков словаря (например, «этот assetId — это токен X с N знаками после запятой»).
Аппаратный сайнер:
- по этим веткам извлекает ровно минимальный нужный кусок метадаты и использует его для человекочитаемого отображения на экране (сумма, токен, тип транзакции и т.п.);
- при подписании коммитится не только к «сырым байтам» транзакции, но и к конкретному Merkle-root метадаты, с которым она была правильно интерпретирована;
- нода, проверяя подпись, знает актуальный Merkle-root метадаты, встроенный в рантайм; - если горячий кошелёк пытался подменить семантику (например, сказал сайнеру, что у токена 2 знака после запятой, а в метадате — 6), подпись не сойдётся при проверке на правильном root, и транзакция просто не будет принята в блок.
В Polkadot уже реализован единый Ledger-апп (Unified App) для всех Substrate-сетей: одно приложение на Ledger умеет корректно парсить разные типы транзакций разных цепочек, опираясь на стандартный механизм метадаты и мерклового root.
Именно блокчейн, я уверен, должен выступать гарантом совпадения того, что вы видите, и того, что вы подписываете. А роль кошельков тут — не запутать пользователя, а выступать посредником, без лишних централизованных сайд-эффектов.
Спасибо за внимание, если знаете еще способы решения этой проблемы, велком в мою телегу.