Глибина безпеки мови Move: характеристики мови, механізм виконання та автоматизований аудит

Вступ

Мова Move є мовою смарт-контрактів, що може працювати в середовищі блокчейну, яке реалізує MoveVM. Вона була створена з урахуванням багатьох проблем безпеки блокчейну та смарт-контрактів, а також з посиланням на частину безпекового дизайну мови RUST. Як нове покоління мов смарт-контрактів, що має безпеку як основну характеристику, яка ж безпека у Move? Чи може вона уникнути звичних загроз безпеці, що походять від віртуальних машин контрактів, таких як EVM та WASM, на мовному рівні або за допомогою відповідних механізмів? Чи існують у неї власні специфічні проблеми безпеки?

У цій статті буде розглянуто питання безпеки мови Move з трьох аспектів: мовні характеристики, механізм роботи та інструменти валідації.

1. Безпекові характеристики мови Move

Написати правильний код дуже складно, навіть після багатьох тестів не можна гарантувати, що код повністю без вразливостей. Коли взаємодієш з ненадійним кодом, написати код, який зберігає ключові властивості безпеки, ще складніше. Існує багато технологій, які можуть забезпечити безпеку під час виконання, таких як пісочниці, ізоляція процесів, об'єктні блокування та інші програмні моделі; також можна вказати статичну безпеку під час компіляції, наприклад, примусове статичне типізування або перевірки тверджень.

Іноді можна також використовувати інструменти семантичного аналізу та статичного аналізу, щоб забезпечити відповідність коду правилам безпеки, навіть під час взаємодії з ненадійним кодом, зберігаючи деякі доведені логічні умови незмінними.

Ці рішення виглядають непогано, можуть уникнути витрат під час виконання та заздалегідь виявити проблеми безпеки. Але, на жаль, безпека, отримана мовами програмування за допомогою цих методів, є вкрай обмеженою, і на це є дві основні причини: по-перше, вони зазвичай мають характеристики, які не можуть бути використані статичними аналітичними інструментами, такі як динамічна диспетчеризація, спільна змінність і рефлексія, які порушують правила безпеки, надаючи хакерам широкий спектр атак. По-друге, більшість мов програмування важко розширити за допомогою статичних інструментів, пов'язаних з безпекою, або виразних формалізованих мов. Незважаючи на те, що ці два розширення дуже важливі, їх можна попередньо визначити.

На відміну від багатьох існуючих мов програмування, мова Move була спроектована з урахуванням підтримки написання програм для безпечної взаємодії з ненадійним кодом, а також підтримки статичної перевірки. Move має такі функції безпеки, оскільки відмовилася від усіх нелінійних логік, що ґрунтуються на гнучкості, не підтримує динамічний диспетчеризацію і не підтримує рекурсивні зовнішні виклики, натомість використовує концепції, такі як узагальнення, глобальне зберігання, ресурси тощо, для реалізації альтернативних моделей програмування. Наприклад, Move пропускає динамічну маршрутизацію та рекурсивні виклики, які в інших мовах смарт-контрактів призводять до дорогих вразливостей повторного входу.

Щоб краще зрозуміти особливості мови Move, розглянемо приклад програми:

перемістити модуль 0x1::TestCoin { використання 0x1::signer;

const ADMIN: адреса = @0x1;

struct Coin має ключ, магазин {
    значення: u64
}

структура Info має ключ {
    total_supply: U64
}

модуль специфікацій {
    інваріант для всіх addr: адреса, де існує<coin>(addr):
        <info>глобальний (ADMIN).total_supply >= глобальний<coin>(addr).value;
}

публічна функція ініціалізації ( облікового запису: & підписувач ) {
    assert!(signer::address_of(account) == ADMIN, 0);
    перемістити_до(рахунок, Інформація { загальна_пропозиція: 0 })
}

публічні розваги mint(account: &signer, amount: u64): Coin набуває Info {
    assert!(signer::address_of(account) == ADMIN, 0);
    
    Нехай пропозиція = borrow_global_mut<info>(ADMIN);
    supply.total_supply = supply.total_supply + сума;
    
    Монета { значення: кількість }
}

Публічні веселощі value(coin: &Coin): U64 {
    Значення монети
}

Публічні веселощі value_mut(coin: &mut Coin): &mut u64 {
    &mut coin.value  
}

}

a) Модуль(Module): Кожен модуль Move складається з ряду визначень структур типів та процесів. Модулі можуть імпортувати визначення типів і викликати процеси, оголошені в інших модулях. Повна кваліфікована назва модуля починається з 16-байтової адреси облікового запису, що містить код модуля. Адреса облікового запису використовується як простір імен для розрізнення модулів з однаковими іменами.

b) Структури (Structs ): Цей модуль визначає дві структури Coin та Info. Coin представляє токени, розподілені користувачеві, а Info фіксує загальну кількість токенів. Обидві структури визначені як типи ресурсів, які можна зберігати у постійних глобальних сховищах ключ/значення.

c) процес(function): код визначає ініціалізацію, безпечний процес та небезпечний процес. Перед створенням Coin необхідно викликати процес initialize, щоб ініціалізувати значення total_supply в одиничному Info на нуль. signer є спеціальним типом, що представляє користувача, перевіреного зовнішньою логікою Move. Утвердження забезпечує, щоб лише вказаний обліковий запис адміністратора міг викликати цей процес. Процес mint дозволяє адміністратору створювати нові токени, також має контроль доступу. Процес value_mut приймає змінну ссылку на Coin і повертає змінну ссылку на поле value.

Структура контракту схожа на інші мови смарт-контрактів, але типи ресурсів та глобальне зберігання є ключовими механізмами зберігання та безпеки ресурсів у мові Move.

Глобальне зберігання дозволяє програмам Move зберігати постійні дані, які можуть бути прочитані та записані програмно лише модулем, що їх має, але зберігаються в публічному реєстрі, де інші користувачі модулів можуть їх переглядати. Кожен ключ у глобальному зберіганні складається з повністю кваліфікованого імені типу та адреси облікового запису, що зберігає значення цього типу. Хоча глобальне зберігання спільне для всіх модулів, кожен модуль має виключне право на читання та запис ключів, які він оголосив.

Модулі, що оголошують типи ресурсів, можуть: • Опублікуйте значення у глобальному сховищі за допомогою команди move_to • Видалити значення з глобального сховища за допомогою команди move_from • Отримання посилання на значення в глобальному хранилищі за допомогою команди borrow_global_mut

Модуль може накладати обмеження на глобальне сховище, яким він керує. Наприклад, забезпечити, щоб лише адреса облікового запису ADMIN могла зберігати структуру типу Info, реалізуючи процес ініціалізації, який використовує move_to на типі Info і накладає умову перед викликом move_to на адресі ADMIN.

Ось два механізми статичної перевірки, які забезпечують безпеку коду цього модуля: зведення невизначеностей та валідація байт-коду.

a) Перевірка інваріантів ( перевірка специфікацій ): рядок 10 модуля вказує на інваріант статичної перевірки — сума значень поля value всіх об'єктів Coin у системі повинна дорівнювати полю total_value об'єкта Info, що зберігається за адресою ADMIN. Інваріант — це термін формальної верифікації, що позначає збереження стану. Ця властивість збереження застосовується до всіх можливих клієнтів модуля.

b) Валідація байт-коду: безпечні типи та лінійність є основними аспектами перевірки байт-коду. Хоча інші модулі не можуть отримати доступ до глобальних елементів сховища, контрольованих 0x1::TestCoin::Coin, вони можуть використовувати цей тип у своїх власних процедурах та деклараціях структур.

Move має валідацію байт-коду (, що забезпечує примусову систему типів на рівні байт-коду ), що дозволяє власникам модулів запобігати небажаним результатам. Лише модулі, які оголосили структуру типу Coin, можуть: • Створити значення типу Coin • "Розпакувати" значення типу Coin в його складові поля • Отримати посилання на поле Coin через змінний або незмінний запозичення в стилі rust

Валідатор також змушує структури за замовчуванням бути лінійними, щоб забезпечити лінійний захист від копіювання та знищення поза модулем, в якому оголошено структуру. Одночасно валідатор також буде примусово перевіряти деякі типові проблеми з пам'яттю.

Процес перевірки складається в основному з трьох категорій:

  1. Перевірка законності структури: забезпечення цілісності байт-коду, виявлення помилок незаконних посилань, повторних ресурсних сутностей та незаконних підписів типів тощо
  2. Семантична перевірка логіки процесу: включає помилки типу параметрів, індекси циклу, порожні індекси та повторне визначення змінних тощо
  3. Помилка при з'єднанні, незаконний виклик внутрішнього процесу, або процеси, що пов'язують декларації та визначення, не відповідають.

Валідатор спочатку створює контрольний граф CFG(, потім перевіряє область доступу викликаного в стосі, щоб забезпечити, що викликаний контракт не може отримати доступ до простору стосу викликача. Одночасно для перевірки типу кожен стек значень підтримує відповідний стек типів.

Далі йде перевірка ресурсів та перевірка посилань. Ресурси в основному перевіряють на недопустимість подвійного витрачення, незнищуваність, обов'язкову належність тощо. Перевірка посилань поєднує динамічний та статичний аналіз, використовуючи механізм перевірки запозичень, подібний до системи типів Rust.

Нарешті, потрібно перевірити посилання, знову перевіривши, чи відповідають об'єкти посилання та декларація, а також контроль доступу до процесу.

![Аналіз безпеки Move: ігрова зміна мовою смарт-контрактів])https://img-cdn.gateio.im/webp-social/moments-419437619d55298077789e6eca578b48.webp(

) 2. Механізм роботи Move

По-перше, програма Move працює у віртуальній машині, і під час виконання не може отримати доступ до системної пам'яті. Це дозволяє Move безпечно працювати в ненадійних середовищах, не піддаючись знищенню чи зловживанню.

По-друге, програма Move виконується на стеку. Глобальна пам'ять поділяється на пам'ять ### купу ( і глобальні змінні ) стек (. Пам'ять є первинною пам'яттю, її одиниці не можуть зберігати вказівники на одиниці пам'яті. Глобальні змінні використовуються для зберігання вказівників на одиниці пам'яті, але спосіб індексації відрізняється. При доступі до глобальних змінних код надає адресу та тип, прив'язаний до цієї адреси. Таке розмежування спрощує операції, що робить семантику мови Move легшою для формалізації.

Команди Move виконуються у стековому інтерпретаторі. Стекова віртуальна машина легко реалізується та контролюється, вимагає менше апаратних ресурсів і підходить для блокчейн-сценаріїв. На відміну від регістрового інтерпретатора, стековий інтерпретатор легше контролює та перевіряє копіювання та переміщення між змінними.

У Move значення, визначене як ресурс, може бути знищено лише шляхом руйнівного переміщення ), що робить попереднє місце зберігання цього значення недійсним (, але деякі значення ), такі як цілі числа (, можуть бути скопійовані.

Коли програма Move працює в стеку, її стан представляє собою чотиривимірний кортеж ⟨C, M, G, S⟩, що складається з викликаного стеку )C(, пам'яті )M(, глобальних змінних )G( та операндів )S(. Стек також підтримує таблицю функцій ) модуля самого ( для розбору інструкцій, що містять тіло функції.

Стек викликів містить всю контекстну інформацію про виконання процесу та номери інструкцій. Під час виконання інструкції Call створюється новий об'єкт стека викликів, параметри виклику зберігаються в пам'яті та глобальних змінних, а потім інтерпретатор виконує інструкції нового контракту. При зустрічі з інструкцією розгалуження відбувається статичний перехід всередині цього процесу. Процеси в межах модуля не мають циклічних залежностей, і оскільки модуль не має динамічного призначення, це зміцнює незмінність викликів функцій під час виконання: фрейми викликів процесу обов'язково сусідні, що запобігає можливості повторного входу. Виклик return завершує виклик, значення повертається на вершину стека.

Досліджуючи код MoveVM, можна помітити, що MoveVM відокремлює зберігання даних і логіку виклику стеку ), що є найбільшою відмінністю від EVM. В EVM реалізація токена ERC20 потребує написання логіки в одному контракті та ведення обліку стану кожного користувача, в той час як в MoveVM стан користувача ( зберігається незалежно під адресою рахунку ), а виклики програм повинні відповідати правилам доступу та обов'язковим правилам щодо ресурсів. Хоча це жертвує певною гнучкістю, проте це сприяє підвищенню безпеки та ефективності виконання, ( що значно покращує можливість паралельного виконання ).

Аналіз безпеки Move: зміна гри для мов смарт-контрактів

( 3. Переходьте до доказувача

Нарешті, давайте розглянемо допоміжний інструмент автоматизованого аудиту Move prover, який надає Move.

Move Prover – це формальний інструмент верифікації на основі міркувань. Він використовує формальну мову для опису поведінки програми та застосовує алгоритми міркування для перевірки, чи відповідає програма очікуванням, допомагаючи розробникам забезпечити правильність смарт-контрактів і зменшити ризики транзакцій. Іншими словами, формальна верифікація – це використання математичних методів для доведення відсутності помилок у системі.

Наразі основні алгоритми автоматичної перевірки програмного забезпечення базуються на рішенні задачі задовільності модульної теорії )SMT solver###. SMT solver насправді є рішателем формул. Верхній рівень алгоритмів перевірки програмного забезпечення розділяє ціль перевірки на формули, які передаються SMT solver для розв'язання, а потім на основі результатів проводиться подальший аналіз і в підсумку звітується про те, чи ціль перевірки виконана, або виявлено контрприклад.

Основний алгоритм верифікації - це дедуктивна верифікація (, а також існують інші алгоритми верифікації, такі як обмежене моделювання, k-індукція, абстракція предикатів та абстракція шляхів тощо.

Move Prover використовує алгоритм дедуктивної перевірки для підтвердження того, чи відповідає програма очікуванням. Це означає, що Move Prover може на основі відомої інформації робити висновки про поведінку програми, забезпечуючи відповідність очікуваній поведінці. Це допомагає забезпечити правильність програми та зменшити обсяг ручного тестування.

Загальна архітектура Move Prover виглядає наступним чином:

По-перше, Move Prover отримує Move-джерело з специфікацією вхідних даних )specification(. Move Parser витягує специфікацію з виходу. Move компілятор компілює вихідний файл у байт-код, разом зі специфікаційною системою перетворює його в модель об'єкта валідатора )Prover Object Model(.

Ця модель була перекладена в проміжну мову Boogie. Код Boogie передається в систему верифікації Boogie для "генерації умов верифікації". Умови верифікації передаються в рішення Z3 ), розроблене Microsoft SMT рішателем (.

Після передачі VC в Z3, валідаційний модуль перевіряє, чи програма SMT формули ) відповідає специфікації (, чи є вона несумісною. Якщо так, то це означає, що специфікація є дійсною. В іншому випадку, генерується модель, що відповідає умовам, яка потім перетворюється назад у формат Boogie для публікації діагностичного звіту. Діагностичний звіт відновлюється у вигляді помилок на рівні виходу, подібних до стандартних компіляторських помилок.

! [MoveAnn.]

MOVE-1.67%
Переглянути оригінал
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
  • Нагородити
  • 5
  • Репост
  • Поділіться
Прокоментувати
0/400
SchrodingerAirdropvip
· 08-09 21:11
move ще не досить безпечний, повірте мені
Переглянути оригіналвідповісти на0
DataBartendervip
· 08-07 02:38
Знову говорять про move так, що не можна в це повірити.
Переглянути оригіналвідповісти на0
AirdropHarvestervip
· 08-07 02:29
Move? Слухати це вже складно, так само важко вчитися, як Rust.
Переглянути оригіналвідповісти на0
MoneyBurnervip
· 08-07 02:28
Знову хоче обманути мене, щоб я увійшов в позицію і подивився на новий блокчейн. Минулого разу я так програв.
Переглянути оригіналвідповісти на0
StableNomadvip
· 08-07 02:28
хмм, ще одна "безпечна" мова... нагадує мені солану у 2021 році, якщо чесно
Переглянути оригіналвідповісти на0
  • Закріпити