December 8, 2022

Как Создать Автоматический Аудитор Смарт-Контракта для Web

В наши дни стоимость аудита продвинутого смарт-контракта может составлять от 50 000 до 100 000 долларов, а иногда и больше, к тому же на проведение комплексного аудита часто уходят месяцы. Так почему бы не переложить большую часть этого процесса на автоматизированного программного аудитора? Мы, разработчики, знаем одну вещь: когда речь идет о проверке сложных и очень подробных вещей, таких как код, компьютеры способны выполнять работу гораздо эффективнее. Я не говорю, что нам не нужны человеческие аудиторы безопасности, я просто считаю, что человеческий аудит должен быть последним шагом после комплексного автоматизированного аудита.

Чтобы проверить эту идею, я решил создать бесплатный автоматизированный веб-аудитор смарт-контрактов с открытым исходным кодом, очень похожий на веб-компилятор Solidity Remix. Пожалуйста, имейте в виду, что этот аудитор все еще примитивен и нуждается в большой работе, и, надеюсь, вклад сообщества блокчейн поможет довести его до уровня зрелой платформы. Но я думаю, что у него есть базовые основы, на которые можно опираться.

Давайте приступим. Я создал фронт-энд аудитора на React, но этот учебник будет в основном посвящен бэк-энд, чтобы сделать его коротким и читабельным. Я начну с общего объяснения того, как я создал аудитора, а затем рассмотрю пример того, как я включил логику аудита в код. В настоящее время аудитор может сканировать код, написанный на solidity, а в будущем планируется расширить его на языки смарт-контрактов Move и Vyper. Я предположу, что вы знаете основы react, terminal, node.js и JavaScript.

Front End: Для фронтальной части я использовал React (create-react-app).
Back End: Node.js / java script. Server: Я развернул свой код на Netflify(serverless). Вы также можете взглянуть на мое веб-приложение здесь и исходный код здесь для более подробной информации.

  1. Базовая установка Перейдите в нужный каталог в командной строке и создайте новый проект react.

    terminal: create-react-app auditorTutorial Удалите все файлы в папке src в только что созданном проекте.

    Создайте файл index.js и папку components, а в ней - файл app.js. Вы можете настроить свою папку src по своему усмотрению, но вы также можете посмотреть мою папку здесь.

    Установите несколько модулей и библиотек.
    BrowserSolc: обертка Solidity, которая может компилировать смарт-контракты в браузере. Web3: будет использоваться для получения дополнительной информации из блокчейна ethereum, такой как примерная стоимость газа. Я использовал infura в качестве провайдера для подключения к узлу ethereum.

    terminal: npm i BrowserSolc, web3, semantic-ui-react Как я уже говорил выше, эта статья будет посвящена в основном бэкенду, поэтому вы можете создать любой макет фронтэнда по своему усмотрению, но если вы используете React, рекомендуется использовать управляемый текстовый компонент, который может хранить текст смарт-контракта, вводимый пользователем. Сначала нам нужно прочитать код смарт-контракта, введенный в браузере. В качестве входа аудитора я использовал семантический компонент реакта с управляемой текстовой областью, содержимое которой хранится в состоянии.

    пример: state = {value: “ ”, contract: “”}; //обновление состояния контракта на основе пользовательского ввода handleChangesToContract=(event)=>{ this.setState({ contract:event.target.value }); } render(){<TextArea value= {this.state.contractCode} onChange= {this.handleChangesToContract} />}
  2. Извлечение кода договора и сохранение в формате массива //пример того, когда запускать аудит, предполагая, что у вас есть кнопка отправки, которая вызывает функцию onSubmit(){ let data = this.state.contract;} Далее я использовал метод javascript '.split("\n")', чтобы разделить код смарт-контракта на массив на основе каждой новой строки.

    onSubmit(){ let data = this.state.contract; let dataArray = source.split(“\n”)} ;
  3. Компилировать код Бэкэнд аудитора состоит из двух различных частей. Во-первых, компилятор solidity запускает код смарт-контракта и предоставляет информацию о любых ошибках и успешной компиляции, во-вторых, набор логики, который проверяет уязвимости безопасности.

    Убедитесь, что BrowserSolc и web3 определены в вашем файле app.js (или там, где вы компилируете файл смарт-контракта). BrowserSolc может быть включен в качестве скрипта в ваш 'public.index.html путем включения :

    <script src=”./browser-solc.min.js” type=”text/javascript”></script> Web3 может быть определен как компонент вашего приложения:

    импортировать Web3 из 'web3';

    Далее мы компилируем код нашего смарт-контракта, который сохраняется в состоянии. Мое состояние хранится под именем 'data'. Вам также понадобится учетная запись infura. Чтобы создать учетную запись, нажмите здесь.

    //Загрузка выбранной версии компилятора window.BrowserSolc.loadVersion(this.state.currentCompiler, async function (compiler){ let optimize = 1; let result = compiler.compile(data, optimize); const provider = new Web3.providers.HttpProvider( ‘https://rinkeby.infura.io/v3/(здесь ваш номер инфуры)' ) const web3 = new Web3(provider); let bytecode = result.contracts[“:SampleContract”].bytecode; } С помощью web3 и байткода, полученного в результате успешной компиляции, можно обращаться к блокчейну ethereum для получения дополнительных данных, таких как расчетная стоимость газа.
  4. Выполнить аудит смарт-контракта Создайте отдельный файл вне компонента приложения и назовите его auditor.js . Это файл, который будет содержать всю логику аудита. Мы будем отправлять массив нашего смарт-контракта в этот файл, проводить аудит и затем отправлять его обратно в наш компонент. Вы можете определить свой файл audit.js следующим образом:

    export default(dataArray)=>{ //определяем одно предупреждение и массив всех обнаруженных предупреждений let warn; let warnings=[]; return warnings; } Исходя из моих исследований, лучшими источниками для поиска логики аудита являются более ранние взломы, такие как взлом DAO и Parity, а также шаблоны, основанные на "лучших практиках смарт-контрактов Ethereum", например, опубликованное компанией consensys руководство по защите смарт-контрактов. В этом руководстве мы будем использовать руководство Consensys в качестве источника логики аудита.

    Давайте разберем код на предмет атаки Reentrancy, которая встречается довольно часто.

    Отрывок с веб-сайта Consensys об атаках на реентерабельность -

    "Одна из главных опасностей вызова внешних контрактов заключается в том, что они могут взять на себя поток управления и внести изменения в ваши данные, которых вызывающая функция не ожидала. Этот класс ошибок может принимать различные формы, и обе основные ошибки, которые привели к краху DAO, были ошибками такого рода".

    "Реентерабельность на одной функции": Первая замеченная версия этой ошибки касалась функций, которые могли вызываться многократно, до завершения первого вызова функции. Это может привести к тому, что различные вызовы функции будут взаимодействовать разрушительным образом."

    В принципе, можно очень быстро выполнить несколько вызовов функции контракта до того, как вся логика функции будет завершена. Например, злоумышленник может сделать несколько снятий средств со счета сверх фактического баланса счета, если баланс счета (состояние) не обновляется перед каждым вызовом функции снятия средств.

    Один из способов предотвратить это - использовать use send() вместо call.value()(). Это ограничит выполнение любого внешнего кода.

    Чтобы перевести это в логику, мы можем просто найти в массиве кода нашего смарт-контракта набор строк “call.value()()". //определите строку, которую вы хотите найти let dangerousCalls1 = ‘.call.value()’; //просмотреть смарт-контракт построчно и сохранить все найденные предупреждения в //массив for (let index=0; index<dataArray.length; index++) { if (dataArray[index].includes(dangerousCalls1)) { warn = {key:(index), value:"Помните, что использование '.call.value()', подвержено атакам повторного входа, по возможности используйте send() или transfer(). Также не забудьте установить баланс вашего нового счета перед переводом"}; warnings.push(warn); } } Затем мы можем вернуть результаты в наш компонент и отобразить все предупреждения пользователю.

    Это очень простой пример поиска уязвимостей в коде, но в принципе любой шаблон можно разбить на строительные блоки и добавить в логику auditor.js. Следует учитывать и другие факторы, например, искомая строка в конкретной строке может быть не вся в одной строке, так в примере выше ".call." и ".value()" могут быть вызваны на разных строках, поэтому более детальная проверка может разделить поиск каждой строки отдельно и проверить следующую "активную" строку, если она содержит определенную строку.

    Как вы можете заметить, для создания надежного бесплатного открытого публичного аудитора потребуется довольно обширная база данных логики, которая может быть создана за счет вклада сообщества в логику аудита.