• 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
  • 🤖 CYBER WEEK-25%
  • До 20 декабря-25%
Про подходы к тестированию с помощью фаззера
«Фаззинг очень полезен – он позволяет находить уязвимости, которые тяжело найти другими инструментами»
Дмитрий Н., Oxorio
22 марта

Фаззинг

Фаззинг – это очень хороший инструмент для анализа безопасности смарт-контракта. Его далеко не все ещё используют, хотя инструмент достаточно простой и эффективный. Он очень полезен – позволяет находить уязвимости, которые тяжело найти другими инструментами.
Echidna как раз и реализует такой способ тестирования, как «фаззинг».

Всего существует четыре основных техники тестирования:

1. Unit testing – юнит-тестирование. В принципе это всем знакомо, так как это достаточно мейнстримный метод тестирования, который знают практически все разработчики.
2. Manual analysis – мануальное тестирование – то, чем мы занимаемся в Oxorio: нам дают код, мы его читаем и ищем там ошибки исходя из своих знаний, опыта, и так далее.
3. Fully automated analysis – автоматическое тестирование. Если мы говорим про код на Solidity, то инструмент, который позволяет реализовать данный метод – это Slither. Просто выпускаем его на коде, и он выдаёт ошибки.
4. Semi-automated analysis – полуавтоматическое тестирование. Как раз фаззинг относится к этому методу.
У всех методов есть свои достоинства и недостатки. Например, юнит-тестирование позволяет находить какие-то ошибки в случае, когда они отклоняются от какого-то набора кейсов, которые мы считаем правильными: код должен работать каким-то определённым образом. Если мы что-то в коде поменяли, и у нас какой-то тест не сработал – значит, произошла какая-то ошибка.
Ручной анализ – это достаточно трудоемкий и дорогой способ проверки кода на уязвимости, потому что нужно взять команду людей, которая будет продолжительное время сидеть, читать этот код, искать в нём ошибки – все это достаточно дорого и не всегда эффективно. Плюс у него есть очень существенный минус: когда вручную анализируется код, как правило, это делается относительно какого-то конкретного коммита. При этом код изменяется, постоянно обновляется, и если для конкретной версии мы убедились, что в коде нет ошибок, нету каких-то уязвимостей, то как только мы сделали следующий коммит, чуть-чуть поменяли код – мы уже не можем гарантировать, что ошибок там нет. Поэтому данный метод работает для какой-то фиксированной версии кода, которая не предполагает обновлений.
Минус автоматического тестирования в том, что оно часто выдает ложно-положительные и ложно-отрицательные результаты. Что-то помогает найти, но, как правило, не супер эффективно, особенно для сложных уязвимостей.
Последний метод – фаззинг – полуавтоматическое тестирование. Полуавтоматическое потому, что мы не можем просто взять код и запустить на нем фаззинг. Нам придётся что-то добавить от себя, что-то дописать. Так же как мы дописываем юнит-тессы, для фаззера мы должны создать некоторый набор условий, инвариантов, которые показывают, как мы хотим, чтобы код работал, и фаззер будет тестировать код относительно этих условий. В этом смысле это похоже на юнит-тестирование.

Как работает

Собственно, как работает фаззер? Очень просто. Он берет наш код, в котором есть набор публичных методов: public или external. У этих методов есть какие-то параметры, и фаззер рандомным образом запускает функции из этого кода и подставляет туда рандомные параметры. После каждого запуска он проверяет тесты и проперти, которые мы написали, что они выполняются и что не нарушаются никакие инварианты, никакие заранее заданные свойства системы. Если они нарушаются при каком-то случайном наборе параметров – значит что-то не так.

Пример

Пример инвариантов или свойств системы, которые можно обнаружить с помощью фаззинга – некорректный аксесс контроль: у контракта есть оунер, которому доступен какой-то набор функций, которые только он, администратор контракта, может вызывать, и предполагается, что какой-то случайный пользователь или третье лицо не может стать этим оунером и выполнять привилегированные функции.
Далеко не всегда с помощью юнит-тестов, например, получается найти такие проблемы. А вот с помощью Echidna можно порой найти то, с чем юнит-тесты не справляются: некорректный стейк контракта, например. Когда мы паузим контракт, предполагается, что невозможно делать какие-то трансферы или операции с токенами. Мы можем задать это как проперти, что если статус контракта – пауза, то токены пересылать нельзя. Если Echidna найдёт какой-то набор вызовов, которые позволят переслать токены, при том, что контракт «запаузен» – мы обнаружим такую уязвимость.
Еще один пример – некорректная арифметика. Например, когда пользователь может получить бесконечное количество токенов в результате вызова каких-либо функций с какими-либо параметрами. При этом, если мы пишем юнит-тесты, нам примерно надо понимать, какие параметры необходимо передать, чтобы такое произошло,и мы действительно это понимаем. Но если система большая, сложная, и там есть много операций с токенами, которые раскиданы в большой кодовой базе, на большом количестве контрактов, то Echidna позволяет эту сложность преодолеть с помощью метода случайного поиска, когда она будет перебирать все возможные методы со всеми возможными параметрами, и проверять каждый раз, что количество токенов на балансе пользователя ограничено каким-то адекватным числом, которое мы задали.
Если это будет не так, то она нам выдаст какой набор методов был выдан, чтобы нарушить такое свойство.

Инварианты

Инварианты/свойства могут быть двух основных типов. Это отражает два подхода к тестированию с помощью фаззера:
1. У нас могут быть достаточно простые инварианты, которые мы можем определить на уровне функции, например, для свойства сложения должно выполняться свойство коммутативности: a+b=b+a. Фаззер как раз может помочь за секунды выявить недостатки такого кода, то есть мы можем протестировать код на уровне функции и проверить ее работоспособность. Этот вид называется function-level invariants.
2. Другой вид свойств – инварианты системного уровня, которые зависят уже от целой системы (system-level invariants). В этом случае нам понадобится уже задеплоенный контракт, мы не сможем протестировать какую-то отдельную компоненту/функцию.

Internal и External testing

Еще одна вещь, которую я хотел бы упомянуть – это internal и external testing. Отличаются они таким образом: если мы хотим протестировать с помощью Echidna какой-нибудь контракт, например, токен – то мы берем этот токен, наследуемся от него и в этом унаследованном файле пишем одно единственное проперти, которое мы проверяем – например баланс пользователя всегда < total supply. Запускаем этот тест с помощью Echidna. Это и будет называться internal-тестированием, потому что у нас есть (когда мы наследуемся) допуск к internal-функциям контракта.
Это работает в простых случаях. Но если у нас не один контракт, а несколько, и они все должны быть задеплоены, в контрактах прокидываются адреса друг на друга и так далее, то такая система уже не сработает, так как мы не сможем унаследоваться и начать вызывать internal-функции. Единственный способ, которым мы можем протестировать в такую систему – это external-тестирование. Когда мы в конструкторе нашего оберточного теста разворачиваем набор контрактов, а затем вызываем публичные и external функции (только они нам и доступны), и каждый раз после вызова этих функций проверяем инварианты – вот это и называется external-тестирование.
Ссылка на презентацию здесь.
Article image
Буткемп: Аудитор смарт-контрактов
От основ Solidity до re-entrancy.
Подробнее
Поделиться
Вам может понравиться
    Интересно погрузиться в крипту?
    Поможем выбрать буткемп!
    или