Неочевидная опасность холодных кошельков

Неочевидная опасность холодных кошельков

0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53

Что за строка выше? Не знаете? И я не знаю. А там — отправка одного эфира на сторонний адрес. Представьте, как неприятно было бы узнать о том, что подписал такое постфактум.

А владельцам базовых Ledger Nano S, Trezor Model T, SafePal S1 и ещё кучи кошельков и представлять не нужно. Для них подобное — реальность.

Ситуация абсурдна. Человек покупает хардверный кошелёк, который весь такой безопасный, пределы которого никогда не покидает сид-фраза, чтобы в итоге в какой-то момент потерять деньги просто потому, что у человека нет уверенности, что он подписывает.

Почему так?

Когда мы хотим осуществить какое-то web3-взаимодействие, мы формируем тело транзакции вида

fields = [ nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0 ]

и формируем hash-to-sign

unsigned_tx_hash = keccak256(rlp_encode(fields))

который и даёт нам ту самую таинственную строку из начала поста. И с точки зрения блокчейна тут нет проблем: сеть знает, от кого, кому, сколько и чего перевести. Проблема в том, что мы не знаем.

Чтобы мы знали, горячие (то есть подключённые к интернету) кошельки научились симулировать транзакции. Они могут это делать, т.к.:

  • имеют 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.

Именно блокчейн, я уверен, должен выступать гарантом совпадения того, что вы видите, и того, что вы подписываете. А роль кошельков тут — не запутать пользователя, а выступать посредником, без лишних централизованных сайд-эффектов.

Спасибо за внимание, если знаете еще способы решения этой проблемы, велком в мою телегу.

Начать дискуссию