Back to Blog

Безопасность экосистемы Solana (4) — Валидация аккаунтов

April 1, 2022
5 min read

0. Обзор

1. Обзор

В предыдущем блоге мы обсуждали, как обновлять программы. В этой статье мы рассмотрим проблемы, связанные с контролем доступа, что является одной из самых распространенных и базовых тем безопасности в сфере DeFi.

2. Инструкции

В Solana каждая программа экспортирует одну точку входа (entrypoint), которая определяется с помощью entrypoint!. В отличие от Ethereum, клиенты могут вызывать только одну функцию, определенную как entrypoint, которая обычно называется process_instruction. Функция entrypoint принимает три параметра: ID программы смарт-контракта, аккаунты, с которыми будет работать программа, и данные инструкции (instruction data). Данные инструкции определяют, какая именно операция будет вызвана. На рисунке ниже приведен пример. Распаковывая данные инструкции, выбираются различные действия (например, Lock или Unlock). Таким образом, инструкции, доступные через entrypoint, являются публичными для всех и могут быть выполнены с использованием определенных данных инструкции.

3. Проверка аккаунтов

Как уже упоминалось, программа получает аккаунты, которые ей необходимо прочитать или изменить. Этот дизайн порождает два вопроса. Во-первых, как гарантировать, что данные, хранящиеся в аккаунтах, предназначенных для чтения, являются доверенными? Во-вторых, как гарантировать, что только привилегированные пользователи могут вызывать инструкции для записи в аккаунты? Далее мы проиллюстрируем проблему контроля доступа. Весь тестовый код можно найти здесь.

3.1 Анализ кода (PrivilegeOwner)

Сначала мы определяем две структуры: Door и Config. Только ключевой аккаунт (строка 17), указанный в структуре door, может открыть созданную «дверь». Однако дверь невозможно открыть, когда состояние системы заблокировано, что указано в структуре Config (строка 81).

Как уже упоминалось, аккаунт Config определяет, можно ли открыть дверь. В этом случае в программе должен быть только один аккаунт Config. Чтобы добиться этого, мы используем PDA для хранения данных Config. После инициализации аккаунта Config мы устанавливаем атрибут is_initialized в значение true, чтобы атакующие не могли инициализировать его повторно (строки 108–110).

Инструкция Open() используется для открытия дверей. Она принимает несколько аккаунтов, включая аккаунт двери, аккаунт config и аккаунт owner, который пытается открыть дверь. Чтобы гарантировать, что дверь принадлежит программе, а конфигурация валидна, мы проверяем владельца аккаунта door и аккаунта config (строки 204–205). Это предотвращает передачу злоумышленниками поддельных аккаунтов. Это отвечает на наш первый вопрос. Чтобы гарантировать, что считываемый аккаунт является доверенным, мы должны проверить его владельца! Обратите внимание, что только владелец аккаунта door может открыть дверь. В данном случае мы проверяем, является ли аккаунт владельца реальным «владельцем» двери и, что более важно, авторизована ли инструкция самим владельцем (строки 217–219).

В функции validate_owner() мы сначала проверяем, совпадают ли публичные ключи этих двух аккаунтов, а затем проверяем подпись владельца. Это отвечает на второй вопрос: чтобы гарантировать использование инструкции open только привилегированными пользователями, нужно проверить владельца и наличие подписи аккаунта. Инструкция close аналогична open, подробности можно найти в коде.

Мы развернули программу в тестовой сети (testnet), ее можно найти по следующей ссылке:

https://explorer.solana.com/address/2Q7FFMWCthBvc6ubLQRx9TRswvaimmd66VaCAfHwsYuC?cluster=testnet

Все тестовые транзакции перечислены ниже. Весь процесс транзакции выглядит так: AllocatePDA() -> InitializeDoor() -> InitializeConfig() -> Unlock() -> Open() -> Close().

https://explorer.solana.com/tx/2X9CyMrHTNEvbzXTE95gem2j8spnvsQsabFeSpV8hiNpYjiQPPzLRqt5KN86ZYRjnQvydvs7y5eUjJK7no8knDhk?cluster=testnet
https://explorer.solana.com/tx/2XfVWiXeQeHbpqAEYm3AH2RU6hunnqtr155EC4EAM5Bq9VVZNP6QocAav9cPjEQdJFcQrbsSSxiKadr4HPMov8pz?cluster=testnet
https://explorer.solana.com/tx/5Em41sg7yFXeNpnEJnhUQJanfLWKwjMqiBeNAqEEzFrSN9P8zKKafcv5F7RKT2pseB171qeoa8Uz4fKgazzayCnW?cluster=testnet
https://explorer.solana.com/tx/2PMtzpSgjnKDLGmRWBdUSFBPimWnudCPekUYbWzPzokENFYa4N4ab4HCtynfGrzswFPTgGYKHU8PccUMHv3mXHkR?cluster=testnet
https://explorer.solana.com/tx/3kviP9MqkWGMV4yA7k7yPQ5BGfXmcYLcctmY1u2D7n56eT1nx8jMtDumkUNJy8yA3KkmzrmfQLjqpigc8ehGZzBN?cluster=testnet
https://explorer.solana.com/tx/38iEaJBzuGMLbfcszdVB8pkniezH8JrA3XGq7JdADZTQ4hNQC82GSTUA2bmcypdVy3t7htWnUzkZ4F8EakmNvqz8?cluster=testnet

3.2 Атакующая транзакция

Чтобы показать важность проверки владельца и подписи, мы приведем два сценария атаки.

Первый сценарий

Первый сценарий: владелец «двери» пытается открыть её, когда config заблокирован. Для этого мы создаем поддельный аккаунт config в другой программе и устанавливаем атрибут is_lock в значение false. Код пользовательской программы приведен ниже.

Мы отправляем транзакцию для создания поддельного аккаунта config, его публичный ключ: 2MtSrbWp24VjPZQcSUkiWrvNro7qqKemVCsh3Yxc8LTy.

https://explorer.solana.com/tx/2qSyrL5gdQXmgGCFzmzMm1StFQRkDgWpss9A9jV11q2fgDGM5C1XRuXvbX1N5Dt3q2pRqnmyXHVtXGF5dqadAzpJ?cluster=testnet

Как только поддельный аккаунт config создан, мы подаем его в программу (строка 423).

Результат показан ниже, лог сообщает: incorrect program id for instruction («неверный ID программы для инструкции»), что означает, что владельцем аккаунта config должна быть сама программа. Таким образом, атакующий не может обойти эту проверку.

Второй сценарий

Второй сценарий: злоумышленник пытается открыть дверь, когда она не заблокирована.

В этом случае мы передаем верный аккаунт владельца в программу (строка 419) и отправляем транзакцию. Результат показан ниже.

Выводится сообщение Signature verification failed («Ошибка проверки подписи»), что означает, что реальный владелец должен подписать транзакцию для открытия двери, поэтому наша вторая атака также терпит неудачу.

4. Резюме

В Solana инструкции реализуют специфическую логику на основе различных аккаунтов, которые передаются клиентами или другими программами. Поэтому надлежащая проверка аккаунтов исключительно важна.

В этой статье мы рассказали, как правильно проверять аккаунты, и использовали два сценария атаки, чтобы проиллюстрировать важность этих проверок. Следите за обновлениями, скоро будет опубликовано больше статей.

Прочитайте другие статьи из этой серии:


О компании BlockSec

BlockSec — это новаторская компания в области безопасности блокчейнов, основанная в 2021 году группой всемирно признанных экспертов по безопасности. Компания стремится повысить безопасность и удобство использования для развивающегося мира Web3, чтобы способствовать его массовому внедрению. С этой целью BlockSec предоставляет услуги аудита безопасности смарт-контрактов и EVM-сетей, платформу Phalcon для разработки систем безопасности и проактивного блокирования угроз, платформу MetaSleuth для отслеживания и расследования транзакций, а также расширение MetaSuites для эффективной работы Web3-разработчиков в криптомире.

На сегодняшний день компания обслужила более 300 уважаемых клиентов, таких как MetaMask, Uniswap Foundation, Compound, Forta и PancakeSwap, и привлекла десятки миллионов долларов США в рамках двух раундов финансирования от выдающихся инвесторов, включая Matrix Partners, Vitalbridge Capital и Fenbushi Capital.

Официальный сайт: https://blocksec.com/

Официальный аккаунт в Twitter: https://twitter.com/BlockSecTeam