mcpField
S
security-audit

security-audit

github.com/aptos-labsセキュリティ2093 回インストール24 週間前に更新
リポジトリ

security-audit について

security-auditは、デプロイ前にMoveコントラクトを確認するスキルです。セキュリティ項目やテストカバレッジを点検し、監査レポートの作成を支援します。

主な機能

  1. 017分野のセキュリティチェックリスト
  2. 02アクセス制御と入力検証の確認
  3. 03オブジェクト・参照・算術・型の安全性確認
  4. 04テストカバレッジとエラー経路の確認
  5. 05監査レポートのテンプレートとコマンド

活用シーン

  1. 01デプロイ前のMoveモジュールの確認
  2. 02署名者とオブジェクト所有権の制御確認
  3. 03算術処理と入力検証のリスク特定
  4. 04セキュリティ監査レポートの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aptos-labs/aptos-agent-skills --skill security-audit

インストール後、Claude Code で/security-auditを入力して読み込まれたか確認します。

詳細

セキュリティ監査スキル

概要

このスキルでは、包括的なチェックリストを使用して Move コントラクトのセキュリティ監査を体系的に実施します。デプロイ前に、すべての項目に合格する必要があります。

重要: セキュリティは妥協できません。ユーザー資金は適切な実装にかかっています。

基本的なワークフロー

ステップ 1:セキュリティチェックリストの実施

以下のすべてのカテゴリを順番に確認します。

  1. アクセス制御 - 関数を呼び出せるのは誰ですか?
  2. 入力検証 - 入力は検証されていますか?
  3. オブジェクトの安全性 - オブジェクトモデルは正しく使用されていますか?
  4. 参照の安全性 - 危険な参照が公開されていませんか?
  5. 算術演算の安全性 - オーバーフロー/アンダーフローは防止されていますか?
  6. ジェネリック型の安全性 - Phantom 型は正しく使用されていますか?
  7. テスト - カバレッジは 100% 達成されていますか?
ステップ 2:アクセス制御の監査

確認項目:

  • すべての entry 関数で署名者の権限を検証している
  • object::owner() でオブジェクトの所有権を確認している
  • 管理者用関数で呼び出し元が管理者であることを確認している
  • 関数の可視性に最小権限の原則を適用している
  • チェックなしで状態を変更する公開関数がない

以下を確認してください:

// ✅ CORRECT: Signer verification
public entry fun update_config(admin: &signer, value: u64) acquires Config {
    let config = borrow_global<Config>(@my_addr);
    assert!(signer::address_of(admin) == config.admin, E_NOT_ADMIN);
    // Safe to proceed
}

// ❌ WRONG: No verification
public entry fun update_config(admin: &signer, value: u64) acquires Config {
    let config = borrow_global_mut<Config>(@my_addr);
    config.value = value; // Anyone can call!
}

オブジェクトについて:

// ✅ CORRECT: Ownership verification
public entry fun transfer_item(
    owner: &signer,
    item: Object<Item>,
    to: address
) acquires Item {
    assert!(object::owner(item) == signer::address_of(owner), E_NOT_OWNER);
    // Safe to transfer
}

// ❌ WRONG: No ownership check
public entry fun transfer_item(
    owner: &signer,
    item: Object<Item>,
    to: address
) acquires Item {
    // Anyone can transfer any item!
}
ステップ 3:入力検証の監査

確認項目:

  • 数値入力がゼロでないことを確認している:assert!(amount > 0, E_ZERO_AMOUNT)
  • 数値入力が最大値以内であることを確認している:assert!(amount <= MAX, E_AMOUNT_TOO_HIGH)
  • ベクターの長さを検証している:assert!(vector::length(&v) > 0, E_EMPTY_VECTOR)
  • 文字列の長さを確認している:assert!(string::length(&s) <= MAX_LENGTH, E_NAME_TOO_LONG)
  • アドレスを検証している:assert!(addr != @0x0, E_ZERO_ADDRESS)
  • 列挙型に相当する値が範囲内であることを確認している:assert!(type_id < MAX_TYPES, E_INVALID_TYPE)

以下を確認してください:

// ✅ CORRECT: Comprehensive validation
public entry fun deposit(user: &signer, amount: u64) acquires Account {
    assert!(amount > 0, E_ZERO_AMOUNT);
    assert!(amount <= MAX_DEPOSIT_AMOUNT, E_AMOUNT_TOO_HIGH);

    let account = borrow_global_mut<Account>(signer::address_of(user));
    assert!(account.balance <= MAX_U64 - amount, E_OVERFLOW);

    account.balance = account.balance + amount;
}

// ❌ WRONG: No validation
public entry fun deposit(user: &signer, amount: u64) acquires Account {
    let account = borrow_global_mut<Account>(signer::address_of(user));
    account.balance = account.balance + amount; // Can overflow!
}
ステップ 4:オブジェクトの安全性の監査

確認項目:

  • ConstructorRef が公開関数から返されない
  • すべての参照(TransferRef、DeleteRef、ExtendRef)がコンストラクターで生成される
  • オブジェクト署名者は、構築時または ExtendRef とともに使用する場合にのみ使われる
  • 明示的に必要な場合を除き、制限のない転送が無効になっている
  • DeleteRef は実際にバーン可能なオブジェクトに対してのみ生成される

以下を確認してください:

// ❌ DANGEROUS: Returning ConstructorRef
public fun create_item(): ConstructorRef {
    let constructor_ref = object::create_object(@my_addr);
    constructor_ref // Caller can destroy object!
}

// ✅ CORRECT: Return Object<T>
public fun create_item(creator: &signer): Object<Item> {
    let constructor_ref = object::create_object(signer::address_of(creator));

    let transfer_ref = object::generate_transfer_ref(&constructor_ref);
    let delete_ref = object::generate_delete_ref(&constructor_ref);
    let object_signer = object::generate_signer(&constructor_ref);

    move_to(&object_signer, Item { transfer_ref, delete_ref });

    object::object_from_constructor_ref<Item>(&constructor_ref)
}
ステップ 5:参照の安全性の監査

確認項目:

  • 公開関数のシグネチャで &mut 参照を公開していない
  • 重要なフィールドを mem::swap から保護している
  • 可変借用のスコープを最小限にしている

以下を確認してください:

// ❌ DANGEROUS: Exposing mutable reference
public fun get_item_mut(item: Object<Item>): &mut Item acquires Item {
    borrow_global_mut<Item>(object::object_address(&item))
    // Caller can mem::swap fields!
}

// ✅ CORRECT: Controlled mutations
public entry fun update_item_name(
    owner: &signer,
    item: Object<Item>,
    new_name: String
) acquires Item {
    assert!(object::owner(item) == signer::address_of(owner), E_NOT_OWNER);

    let item_data = borrow_global_mut<Item>(object::object_address(&item));
    item_data.name = new_name;
}
ステップ 6:算術演算の安全性の監査

確認項目:

  • 加算時のオーバーフローをチェックしている
  • 減算時のアンダーフローをチェックしている
  • ゼロ除算を防止している
  • 乗算時のオーバーフローをチェックしている

以下を確認してください:

// ✅ CORRECT: Overflow protection
public entry fun deposit(user: &signer, amount: u64) acquires Account {
    let account = borrow_global_mut<Account>(signer::address_of(user));

    // Check overflow BEFORE adding
    assert!(account.balance <= MAX_U64 - amount, E_OVERFLOW);

    account.balance = account.balance + amount;
}

// ✅ CORRECT: Underflow protection
public entry fun withdraw(user: &signer, amount: u64) acquires Account {
    let account = borrow_global_mut<Account>(signer::address_of(user));

    // Check underflow BEFORE subtracting
    assert!(account.balance >= amount, E_INSUFFICIENT_BALANCE);

    account.balance = account.balance - amount;
}

// ❌ WRONG: No overflow check
public entry fun deposit(user: &signer, amount: u64) acquires Account {
    let account = borrow_global_mut<Account>(signer::address_of(user));
    account.balance = account.balance + amount; // Can overflow!
}
ステップ 7:ジェネリック型の安全性の監査

確認項目:

  • 型証明子に Phantom 型を使用している:struct Vault<phantom CoinType>
  • ジェネリック制約が適切である:<T: copy + drop>
  • 型の混同が起こり得ない

以下を確認してください:

// ✅ CORRECT: Phantom type for safety
struct Vault<phantom CoinType> has key {
    balance: u64,
    // CoinType only for type safety, not stored
}

public fun deposit<CoinType>(vault: Object<Vault<CoinType>>, amount: u64) {
    // Type-safe: can't deposit BTC into USDC vault
}

// ❌ WRONG: No phantom (won't compile if CoinType not in fields)
struct Vault<CoinType> has key {
    balance: u64,
}
ステップ 8:テストの監査

確認項目:

  • 行カバレッジを 100% 達成している:aptos move test --coverage
  • すべてのエラーパスを #[expected_failure] でテストしている
  • 複数の署名者を使ってアクセス制御をテストしている
  • 無効な入力を使って入力検証をテストしている
  • エッジケース(最大値、空のベクターなど)を網羅している

実行:

aptos move test --coverage
aptos move coverage source --module <module_name>

出力でカバレッジが 100% と表示されることを確認してください。

セキュリティ監査レポートのテンプレート

以下の形式でレポートを作成してください。

# Security Audit Report

**Module:** my_module **Date:** 2026-01-23 **Auditor:** AI Assistant

## Summary

- ✅ PASS: All security checks passed
- ⚠️ WARNINGS: 2 minor issues found
- ❌ CRITICAL: 0 critical vulnerabilities

## Access Control

- ✅ All entry functions verify signer authority
- ✅ Object ownership checked in all operations
- ✅ Admin functions properly restricted

## Input Validation

- ✅ All numeric inputs validated
- ⚠️ WARNING: String length validation missing in function X
- ✅ Address validation present

## Object Safety

- ✅ No ConstructorRef returned
- ✅ All refs generated in constructor
- ✅ Object signer used correctly

## Reference Safety

- ✅ No public &mut references
- ✅ Critical fields protected

## Arithmetic Safety

- ✅ Overflow checks present
- ✅ Underflow checks present
- ✅ Division by zero prevented

## Generic Type Safety

- ✅ Phantom types used correctly
- ✅ Constraints appropriate

## Testing

- ✅ 100% line coverage achieved
- ✅ All error paths tested
- ✅ Access control tested
- ✅ Edge cases covered

## Recommendations

1. Add string length validation to function X (line 42)
2. Consider adding event emissions for important state changes

## Conclusion

✅ Safe to deploy after addressing warnings.

一般的な脆弱性

脆弱性検出方法影響修正方法
アクセス制御の欠如エントリー関数に assert!(signer...) がない致命的 — 誰でも呼び出せます署名者の検証を追加する
所有権チェックの欠如assert!(object::owner...) がない致命的 — 誰でも任意のオブジェクトを変更できます所有権チェックを追加する
整数オーバーフロー加算前にチェックがない致命的 — 残高が 0 にラップしますassert!(a <= MAX - b, E_OVERFLOW) をチェックする
整数アンダーフロー減算前にチェックがない致命的 — 残高が MAX にラップしますassert!(a >= b, E_UNDERFLOW) をチェックする
ConstructorRef の返却関数が ConstructorRef を返す致命的 — 呼び出し元がオブジェクトを破棄できます代わりに Object<T> を返す
&mut の公開公開関数が &mut T を返す高 — mem::swap 攻撃特定の操作のみを公開する
入力検証なし任意の値を受け入れる中 — ゼロの金額、オーバーフローすべての入力を検証する
テストカバレッジの低さカバレッジが 100% 未満中 — 本番環境でバグが発生しますテストを追加する

自動チェック

監査の一環として、次のコマンドを実行します。

# Compile (check for errors)
aptos move compile

# Run tests
aptos move test

# Check coverage
aptos move test --coverage
aptos move coverage summary

# Expected: 100.0% coverage

手動チェック

コードで次の点を確認します。

  1. アクセス制御:

    • entry fun を検索 → それぞれに署名者チェックがあることを確認します
    • borrow_global_mut を検索 → 使用前に認可チェックがあることを確認します
  2. 入力検証:

    • 関数のパラメーターを検索 → 検証されていることを確認します
    • amount、length、address パラメーターを確認 → チェックがあることを確認します
  3. オブジェクトの安全性:

    • ConstructorRef を検索 → 決して返されないことを確認します
    • create_object を検索 → 参照が適切に生成されることを確認します
  4. 算術演算:

    • + を検索 → オーバーフローチェックがあることを確認します
    • - を検索 → アンダーフローチェックがあることを確認します
    • / を検索 → ゼロ除算チェックがあることを確認します

必ず守るルール

  • ✅ デプロイ前に必ずセキュリティチェックリスト全体を実施します
  • ✅ 必ずテストカバレッジが 100% であることを確認します
  • ✅ エントリー関数のアクセス制御を必ず確認します
  • ✅ すべての入力を必ず検証します
  • ✅ オーバーフロー/アンダーフローを必ず防止します
  • ✅ 必ず監査レポートを作成します
  • ✅ デプロイ前に必ず重大な問題を修正します

決してしてはいけないこと

  • ❌ デプロイ前のセキュリティ監査を決して省略しません
  • ❌ セキュリティチェックの失敗を決して無視しません
  • ❌ テストカバレッジが 100% 未満の状態で決してデプロイしません
  • ❌ 重大な脆弱性を含むコードを決して承認しません
  • ❌ セキュリティレビューを決して急ぎません
  • ❌ 監査中に ~/.aptos/config.yaml または .env ファイルを決して読みません(秘密鍵が含まれています)
  • ❌ 監査中に見つかった秘密鍵の値を決して表示または繰り返しません

参考資料

パターンのドキュメント:

  • ../../../patterns/move/SECURITY.md - 包括的なセキュリティガイド
  • ../../../patterns/move/OBJECTS.md - オブジェクトの安全性に関するパターン

公式ドキュメント:

関連スキル:

  • generate-tests - テストが存在することを確認します
  • write-contracts - セキュリティパターンを適用します
  • deploy-contracts - デプロイ前の最終チェックを行います

重要: セキュリティに妥協は許されません。チェックリストの各項目に合格する必要があります。ユーザーの資金がかかっています。

コメント