- PVSM.RU - https://www.pvsm.ru -
С этой статьи мы начинаем цикл, посвященный типичным уязвимостям, атакам и проблемным местам, присущим смарт-контрактам на языке Solidity, и платформе Ethereum в целом. В первой части мы поговорим вот о чем:
Disclaimer: О front-running attack мы уже рассказывали [1] в разборе конкурса для ZeroNights 2017. Поэтому те, кто читал, могут сразу переходить к следующему пункту.
Термин front-running [2] появился уже давно, и означает возможность манипуляции на рынке за счет обладания закрытой информацией о транзакциях в состоянии ожидания (pending). Если мошенник в курсе того, что грядет большая закупка, он может быстро скупить предмет торга по дешевке, таким образом гарантируя себе выгоду.
В криптовалютах, и в Ethereum в частности, все транзакции сначала помещаются в пул неподтвержденных (pending pool или mempool или backlog), где ожидают, пока майнер возьмет их оттуда и добавит в блок. Однако, в отличие от классических бирж, где такая информация доступна очень узкому кругу лиц, pending pool в Ethereum могут видеть все участники сети. И, поскольку среднее время, через которое новая транзакция попадет в блок, составляет ~14 секунд [3], у атакующего достаточно времени, чтобы проанализировать поведение рынка и послать собственную транзакцию, учитывающую это поведение. Последнее — как атакующий может гарантировать, что его транзакция будет обработана в первую очередь? Ответ кроется в размере комиссии за транзакцию — чем она больше, тем быстрее транзакция попадет в блок. Однако, в Ethereum понятие комиссии немного сложнее, чем обычно, и высчитывается следующим образом:
где gas — это некая единица топлива для EVM [4], которая расходуется при исполнении смарт-контракта и сохранении данных в блокчейн. Таким образом, на самом деле атакующий может, манипулируя ценой за единцу газа (gasPrice), добиться того, чтобы для биржи его транзакция была первой. Тут [1] и тут [5] есть пример проведения такой атаки против смарт-контракта.
В качестве mitigation от этой атаки смарт-контракт может анализировать свойство транзакции tx.gasprice
. Однако, это не будет полным решением проблемы, поскольку майнер на самом деле не обязан сортировать транзакции по убыванию gasPrice — это лишь экономический стимул. Если вдруг появится вариант более значительного стабильного выигрыша, кто знает, чем займется майнер :)
Чтобы хоть как-то защититься от этого, можно использовать криптографию — например, посылать сначала хеш от желаемого действия (покупка или продажа) с количеством токенов. А в следующем блоке уже отправлять сами данные. Хотя схема тоже не лишена недостатков, как минимум, она более сложная — требует проводить в два раза больше транзакций.
Ethereum по своему дизайну — детерминированный механизм, поэтому внутри него очень сложно где-либо черпать энтропию. Однако не все разработчики на Solidity являются искушенными во внутреннем устройстве, а видят перед собой лишь описание синтаксиса языка, и пытаются применить его так, как они это делали в других языках программирования.
Итак, откуда нельзя брать энтропию:
Исключение составляет функция block.blockhash(uint blockNumber)
, которая возвращает хеш блока по его номеру. Однако, применять ее нужно, держа в уме две вещи:
Примеры неправильного использования blockhash и эксплоиты к ним можно посмотреть тут [7] и тут [8].
Другой вариант — схема commit-reveal, которую использует RANDAO [9]. В первой фазе в течение M блоков N участников загадывают случайное число и отправляют смарт-контракту хеш от него с некоторым депозитом. Во второй фазе участники посылают свои загаданные числа смарт-контракту, а тот проверяет число, взяв от него хеш. После того, как все отправили числа, контракт использует их как seed для PRNG. Если участник в заданное время не присылает свое число, он лишается того депозита, который внес, а раунд отменяется (остальные получают свой депозит назад). Недостаток схемы очевиден — она подвержена DOS, поэтому если случайные числа нужны постоянно и незамедлительно, такая схема в чистом виде вряд ли подойдет. Стоит присмотреться к дополнительным правилам [10] для этой схемы, как предлагают сами RANDAO или придумать свою на ее основе, как Виталик [11]. А можно объединить [12] идею с хешом блока и схемой commit-reveal.
Еще один вариант, заслуживающий внимания — Signidice [13]. Схема хороша, когда участников немного, поэтому мы рассмотрим ее на примере игры в рулетку. Итак, есть два участника — казино и игрок, а так же смарт-контракт, который реализует логику игры. На подготовительном этапе казино генерирует пару приватный-открытый ключ и отправляет публичный смарт-контракт. На этом подготовка окончена, можно играть. Поехали:
Самый большой недостаток, который останавливает от того, чтобы брать и применять схему прямо сейчас, — это то, что алгоритм подписи у Ethereum — ECDSA [14]. И если использовать его, то у казино всегда будет возможность читерить. Согласну алгоритму [15], на третьем шаге выбирается случайное k. Этот параметр напрямую влияет на итоговую подпись, причем нельзя использовать одно и то же k, иначе, имея две подписи, можно будет восстановить приватный ключ казино (раскрывать k нельзя по той же причине). Поэтому казино может менять k до тех пор, пока не получит такую подпись, с которой оно выиграет. Вот тут [16] есть пример такого казино.
К тому же (отбросим предыдущую проблему), необходимо решить вопрос с тем, как быть, если участник загадает то же самое число. При условии, что казино использует те же параметры для подписи (в том числе, k), она будет идентична предыдущей, а значит, не случайна. Поэтому нужен запрет на переиспользование загадываемого числа или генерация новой пары ключей каждый раунд.
"Светом в конце тоннеля" для Signidice является EIP-198 [17] о добавлении операции взятие по модулю. Это делает возможным реализацию проверки подписи для RSA. При использовании RSA читерить казино не удастся.
На самом деле, подходов к решению задачи получения случайных чисел много, за бортом остались варианты с получением энтропии off-chain (Oraclize [18]) и эксперименты с whisper [19].
В этом блоке мы не будем касаться смарт-контрактов, однако рассмотрим одну особенность permissioned blockchain — валидаторы всегда известны. Как пример, сеть с Proof-of-Authority консенсусом. Proof-of-Authority — это частный случай сети с Proof-of-work консенсусом, только майнить могут лишь избранные ноды (валидаторы). Яркий пример таких сетей — это тестовые сети для разработчиков Kovan и Rinkbey. Придуман такой консенсус главным образом для того, чтобы избежать спам атак [20]. Когда злонамеренный майнер, имея преимущество в мощности (за счет использования GPU), добывает новые блоки быстрее остальной сети, он консолидирует в своих руках большое количество ether. В то время как обычные участники сети не могут больше добывать ether самостоятельно — они осушают "краны", которые ранее пополнялись добросовестными майнерами. Все это приводит к тому, что новые разработчкики не могут пользоваться сетью из-за отсутсвия ether. Для тех, у кого эфир все же есть, нормальная работа в такой сети тоже не представляется возможной. Майнер способен замусорить сеть бессмысленными, но дорогими транзакциями, что, в свою очередь, сделает добавление в блок транзакций нормальных участников сети затруднительным и долгим.
Так вот, Proof-of-Authority подход с избранными валидаторами может применяться не только для тестовых сетей, но так же для permissioned-сетей. Например PoA network [21] — публично доступная сеть, в которой валидаторами являются юридически закрепленные участники. Избавляет ли PoA эти сети от возможности проведения DOS атак? И да, и нет.
На уровне сети интернет все еще есть возможность сгенерировать большой объем трафика на порт 30303, на котором слушает майнер. И тем самым сделать его недоступным для остальной сети. Далее очевидно — нет валидатора, никто не добывает блоки, транзакции копятся, сеть стоит. Но как же вычислить майнера в сети? Ведь он использует точно такой же клиент для сети, как и остальные.
Алгоритм на самом деле прост:
curl --data '{"method":"parity_netPeers","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545 -s | jq '.result.peers[]' | jq '.network.remoteAddress' | cut -d """ -f 2 | cut -d ":" -f 1
Если их более 25, то придется принудительно рвать соединения до известных IP, например, с помощью iptables, и так собрать их все.
Далее все, что нужно, — это отслеживать, с каких IP первыми приходят новые блоки. Эти IP и будут майнеры.
На этом пока что все. В следующей части перейдем непосредственно к проблемам, которые могут встретиться в смарт-контрактах.
Автор: p4lex
Источник [22]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/informatsionnaya-bezopasnost/272135
Ссылки в тексте:
[1] рассказывали: https://habrahabr.ru/company/dsec/blog/343534/
[2] front-running: https://en.wikipedia.org/wiki/Front_running
[3] ~14 секунд: https://etherscan.io/chart/blocktime
[4] EVM: https://github.com/ethereum/wiki/wiki/Glossary
[5] тут: https://hackernoon.com/front-running-bancor-in-150-lines-of-python-with-ethereum-api-d5e2bfd0d798
[6] глобальных переменных:: http://solidity.readthedocs.io/en/develop/miscellaneous.html#global-variables
[7] тут: https://github.com/slotthereum/source/issues/1
[8] тут: https://github.com/pertsev/solidity_tricks/tree/master/BlockHashDependency
[9] RANDAO: https://github.com/randao/randao
[10] дополнительным правилам: https://github.com/randao/randao#additional-rules
[11] Виталик: https://www.reddit.com/r/ethereum/comments/4mdkku/could_ethereum_do_this_better_tor_project_is/d3v6djb/
[12] объединить: https://blog.winsome.io/random-number-generation-on-winsome-io-future-blockhashes-fe44b1c61d35
[13] Signidice: https://github.com/gluk256/misc/blob/master/rng4ethereum/signidice.md
[14] ECDSA: https://ru.wikipedia.org/wiki/ECDSA
[15] алгоритму: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
[16] тут: https://github.com/pertsev/web3_utilz/tree/master/ECDSA%20signature%20generating%20(cheating)
[17] EIP-198: https://github.com/ethereum/EIPs/pull/198
[18] Oraclize: https://blog.oraclize.it/the-random-datasource-a-scalable-architecture-for-on-demand-untrusted-delivery-of-entropy-7dbae6536322
[19] whisper: https://medium.com/@kkenji1024/designing-random-number-generators-41e653782a7f
[20] спам атак: https://www.coindesk.com/ethereum-spam-attacks-back-time-test-network/
[21] PoA network: https://medium.com/oracles-network/introducing-oracles-network-864d1d7e37e2
[22] Источник: https://habrahabr.ru/post/345196/?utm_source=habrahabr&utm_medium=rss&utm_campaign=345196
Нажмите здесь для печати.