security-audit について
security-auditは、デプロイ前にMoveコントラクトを確認するスキルです。セキュリティ項目やテストカバレッジを点検し、監査レポートの作成を支援します。
主な機能
- 017分野のセキュリティチェックリスト
- 02アクセス制御と入力検証の確認
- 03オブジェクト・参照・算術・型の安全性確認
- 04テストカバレッジとエラー経路の確認
- 05監査レポートのテンプレートとコマンド
活用シーン
- 01デプロイ前のMoveモジュールの確認
- 02署名者とオブジェクト所有権の制御確認
- 03算術処理と入力検証のリスク特定
- 04セキュリティ監査レポートの作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ aptos-labs/ aptos-agent-skills --skill security-auditインストール後、Claude Code で/security-auditを入力して読み込まれたか確認します。
詳細
セキュリティ監査スキル
概要
このスキルでは、包括的なチェックリストを使用して Move コントラクトのセキュリティ監査を体系的に実施します。デプロイ前に、すべての項目に合格する必要があります。
重要: セキュリティは妥協できません。ユーザー資金は適切な実装にかかっています。
基本的なワークフロー
ステップ 1:セキュリティチェックリストの実施
以下のすべてのカテゴリを順番に確認します。
- アクセス制御 - 関数を呼び出せるのは誰ですか?
- 入力検証 - 入力は検証されていますか?
- オブジェクトの安全性 - オブジェクトモデルは正しく使用されていますか?
- 参照の安全性 - 危険な参照が公開されていませんか?
- 算術演算の安全性 - オーバーフロー/アンダーフローは防止されていますか?
- ジェネリック型の安全性 - Phantom 型は正しく使用されていますか?
- テスト - カバレッジは 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
手動チェック
コードで次の点を確認します。
-
アクセス制御:
entry funを検索 → それぞれに署名者チェックがあることを確認しますborrow_global_mutを検索 → 使用前に認可チェックがあることを確認します
-
入力検証:
- 関数のパラメーターを検索 → 検証されていることを確認します
amount、length、addressパラメーターを確認 → チェックがあることを確認します
-
オブジェクトの安全性:
ConstructorRefを検索 → 決して返されないことを確認しますcreate_objectを検索 → 参照が適切に生成されることを確認します
-
算術演算:
+を検索 → オーバーフローチェックがあることを確認します-を検索 → アンダーフローチェックがあることを確認します/を検索 → ゼロ除算チェックがあることを確認します
必ず守るルール
- ✅ デプロイ前に必ずセキュリティチェックリスト全体を実施します
- ✅ 必ずテストカバレッジが 100% であることを確認します
- ✅ エントリー関数のアクセス制御を必ず確認します
- ✅ すべての入力を必ず検証します
- ✅ オーバーフロー/アンダーフローを必ず防止します
- ✅ 必ず監査レポートを作成します
- ✅ デプロイ前に必ず重大な問題を修正します
決してしてはいけないこと
- ❌ デプロイ前のセキュリティ監査を決して省略しません
- ❌ セキュリティチェックの失敗を決して無視しません
- ❌ テストカバレッジが 100% 未満の状態で決してデプロイしません
- ❌ 重大な脆弱性を含むコードを決して承認しません
- ❌ セキュリティレビューを決して急ぎません
- ❌ 監査中に
~/.aptos/config.yamlまたは.envファイルを決して読みません(秘密鍵が含まれています) - ❌ 監査中に見つかった秘密鍵の値を決して表示または繰り返しません
参考資料
パターンのドキュメント:
../../../patterns/move/SECURITY.md- 包括的なセキュリティガイド../../../patterns/move/OBJECTS.md- オブジェクトの安全性に関するパターン
公式ドキュメント:
関連スキル:
generate-tests- テストが存在することを確認しますwrite-contracts- セキュリティパターンを適用しますdeploy-contracts- デプロイ前の最終チェックを行います
重要: セキュリティに妥協は許されません。チェックリストの各項目に合格する必要があります。ユーザーの資金がかかっています。
コメント
Markdown 対応