セキュリティ設計ドキュメント
このドキュメントでは、secretctl のセキュリティアーキテクチャ、設計決定、実装詳細の包括的な概要を提供します。セキュリティ意識の高いユーザー、監査者、コントリビューター向けです。
エグゼクティブサマリー
secretctl は以下のコア原則で多層防御セキュリティアーキテクチャを実装しています:
| 原則 | 実装 |
|---|---|
| AI安全設計 | AI エージェントは平文シークレットを受け取らない |
| ローカルファースト | クラウド依存なし、すべてのデータはマシン上に |
| 標準暗号 | AES-256-GCM + Argon2id(OWASP 準拠) |
| 最小信頼 | Go stdlib + golang.org/x/crypto のみ |
| 完全な監査可能性 | HMAC チェーンによる改ざん検出可能なログ |
アーキテクチャ概要
┌─────────────────────────────────────────────────────────────────────────────┐
│ secretctl セキュリティアーキテクチャ │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ アクセス層 │ │
│ │ │ │
│ │ CLI デスクトップアプリ MCP サーバー │ │
│ │ ──── ────────────── ────────── │ │
│ │ 人間信頼 人間信頼 AI 信頼 │ │
│ │ フルアクセス フルアクセス 制限付き │ │
│ │ (AI安全設計) │ │
│ └──────────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────┴──────────────────────────────────────┐ │
│ │ ポリシー層 │ │
│ │ │ │
│ │ MCP ポリシーエンジン コマンド検証 レート制限 │ │
│ │ ───────────────── ────────────── ───────────── │ │
│ │ allowed_keys blocked_commands 5同時実行 │ │
│ │ denied_keys タイムアウト強制 最大実行数 │ │
│ │ capabilities 非TTYモード │ │
│ └──────────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────┴──────────────────────────────────────┐ │
│ │ 暗号層 │ │
│ │ │ │
│ │ 鍵導出 暗号化 整合性 │ │
│ │ ────────── ────────── ───────── │ │
│ │ Argon2id AES-256-GCM HMAC-SHA256 │ │
│ │ 64MBメモリ ランダムnonce チェーン検証 │ │
│ │ 3イテレーション シークレットごと │ │
│ │ 4スレッド │ │
│ └──────────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────┴──────────────────────────────────────┐ │
│ │ ストレージ層 │ │
│ │ │ │
│ │ vault.db (0600) vault.salt (0600) audit.log (0600) │ │
│ │ ───────────────── ──────────────── ──────────────── │ │
│ │ 暗号化シークレット 128ビットランダム HMACチェーン │ │
│ │ 暗号化DEK Vaultごと 改ざん検出 │ │
│ │ メタデータ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
鍵階層
secretctl は防御を深めるために3層の鍵階層を使用します:
ユーザー入力: マスターパスワード
│
▼ Argon2id (m=64MB, t=3, p=4, salt=128ビット)
│
マスターキー (256ビット、メモリのみ)
│
▼ AES-256-GCM 暗号化/復号
│
データ暗号化キー (256ビット、暗号化して保存)
│
▼ AES-256-GCM 暗号化/復号
│
暗号化されたシークレット (SQLite に保存)
設計の理由
| レイヤー | 目的 | セキュリティ特性 |
|---|---|---|
| マスターパスワード | ユーザー認証 | 保存されない、復元不可 |
| マスターキー | DEK を保護 | 永続化されない、アンロック時に導出 |
| DEK | シークレットを暗号化 | 再暗号化なしでパスワードローテーション可能 |
| 暗号化シークレット | 保護されたデータ | GCM による機密性 + 整合性 |
パスワードローテーションサポート
DEK レイヤーにより、すべてのシークレットを再暗号化せずにパスワード変更が可能:
旧パスワード ─▶ 旧マスターキー ─▶ DEK を復号
│
▼
新パスワード ─▶ 新マスターキー ─▶ DEK を再暗号化
暗号仕様
Argon2id パラメータ(OWASP 2025 準拠)
| パラメータ | 値 | 理由 |
|---|---|---|
| メモリ | 64 MB | セキュリティとユーザビリティのバランス |
| イテレーション | 3 | インタラクティブ使用に推奨 |
| 並列性 | 4 | 典型的な CPU コア数 |
| Salt | 128 ビット | Vault ごとにユニーク |
| 出力 | 256 ビット | AES-256 キー要件 |
なぜ Argon2id か?
- メモリハード: GPU/ASIC 攻撃に耐性
- ハイブリッド: Argon2i(サイドチャネル耐性)と Argon2d(GPU 耐性)を組み合わせ
- 標準: パスワードハッシュコンペティション優勝者
AES-256-GCM
| プロパティ | 値 |
|---|---|
| キーサイズ | 256 ビット |
| Nonce | 96 ビット、暗号化ごとにランダム |
| タグ | 128 ビット |
| モード | GCM(認証付き暗号化) |
なぜ AES-256-GCM か?
- 認証付き: 機密性と整合性の両方を提供
- 標準: NIST 承認、広く監査済み
- 高性能: 現代の CPU でハードウェアアクセラレーション
HMAC チェーン(監査ログ)
レコード N:
hmac = HMAC-SHA256(
id || op || key || timestamp || result || prev,
audit_key
)
audit_key = HKDF-SHA256(master_key, "audit-log-v1", 32)
AI安全設計
コア原則
AI エージェントはシークレットを見ずに使用する。
これは 1Password の「Access Without Exposure」哲学に従っています。
実装
┌─────────────────────────────────────────────────────────────────────────────┐
│ AI安全設計実装 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 禁止(未実装) 許可(制限付き) │
│ ════════════════════════ ════════════════════════════ │
│ │
│ secret_get (平文) secret_list (名前のみ) │
│ secret_set (書き込み) secret_exists (メタデータ) │
│ secret_delete (破壊的) secret_get_masked (****WXYZ) │
│ export (一括アクセス) secret_run (環境変数注入) │
│ secret_list_fields (フィールド名) │
│ secret_get_field (非機密) │
│ secret_run_with_bindings │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
secret_run セキュリティレイヤー
AI リクエスト: secret_run(keys=["aws/*"], command="aws s3 ls")
│
▼
┌─────────────────────────────────────────────────────┐
│ レイヤー 1: 入力検証 │
│ • コマンドブロックリスト (env, printenv, export など) │
│ • キーパターン検証 │
│ • ポリシーエンジンチェック │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ レイヤー 2: 実行分離 │
│ • クリーンな環境でのサブプロセス │
│ • 非TTYモード強制 │
│ • タイムアウト: 設定可能、最大1時間 │
│ • 一時的な作業ディレクトリ │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ レイヤー 3: シークレット注入 │
│ • シークレットは環境変数として設定 │
│ • 値はサブプロセスメモリにのみ存在 │
│ • AI コンテキストを通過しない │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ レイヤー 4: 出力サニタイズ │
│ • stdout/stderr でシークレット値をスキャン │
│ • マッチを [REDACTED:key] で置換 │
│ • レスポンスに sanitized フラグを返却 │
└───────────────────────┬─────────────────────────────┘
│
▼
AI レスポンス: {"exit_code": 0, "stdout": "bucket1\n", "sanitized": true}
ブロックされたコマンド
環境変数を露出する可能性のあるコマンドはブロック:
var defaultDeniedCommands = []string{
"env", // すべての環境変数を一覧
"printenv", // 環境変数を出力
"set", // 環境を表示するシェル組み込み
"export", // 環境用シェル組み込み
"cat /proc/*/environ", // Linux プロセス環境
}
ポリシーエンジン
設定
ポリシーエンジンは secret_run で実行できるコマンドを制御:
# ~/.secretctl/mcp-policy.yaml
version: 1
default_action: allow # allow | deny
denied_commands: # 常にブロック(デフォルトに追加)
- rm
- dd
allowed_commands: # 明示的に許可(default_action: deny 時)
- aws
- kubectl
env_aliases: # 環境固有のキー解決
dev:
- pattern: "db/*"
target: "db/dev/*"
prod:
- pattern: "db/*"
target: "db/prod/*"
評価順序
- デフォルト拒否を最初に: 組み込みブロックコマンドをチェック(env、printenv など)
- ユーザー拒否コマンド:
denied_commandsパターンをチェック - 許可コマンド:
default_action: denyの場合、allowed_commandsをチェック - デフォルトアクション: 明示的なマッチがない場合
default_actionを適用 - 監査ログ: アクセス試行を記録
キーレベルアクセス制御
現在のポリシーエンジンはコマンド実行を制御し、キーレベルアクセスは制御しません。フィールド感度(機密 vs 非機密)は AI安全設計を通じて MCP ツールレベルで強制されます。将来のバージョンでキーごとのアクセスポリシーを追加する可能性があります。
監査システム
ログストレージ
監査ログは月次 JSONL ファイルとメタデータファイルとして保存:
~/.secretctl/
├── audit/
│ ├── audit.meta # チェーン状態(シーケンス、prevHash)
│ ├── 2025-01.jsonl # 2025年1月イベント
│ └── 2025-02.jsonl # 2025年2月イベント
ログエントリ構造
{
"v": 1,
"id": "01HQ5E7N8KM...",
"ts": "2025-01-15T10:30:00.123456789Z",
"op": "run",
"key": "aws/credentials",
"key_hmac": "a3f2b1...",
"actor": {
"type": "user",
"source": "mcp",
"session_id": "..."
},
"result": "success",
"ctx": {
"command": "aws s3 ls",
"exit_code": 0
},
"chain": {
"seq": 42,
"prev": "7c8d4e...",
"hmac": "b4e5f6..."
}
}
整合性検証
$ secretctl audit verify
✓ 15,234 レコードを検証
✓ チェーン整合性: OK
✓ ギャップは検出されず
# 改ざんが検出された場合:
$ secretctl audit verify
✗ レコード id=01HQ5E7N8K... でチェーン破損
期待される prev: a3f2b1...
実際の prev: 7c8d4e...
アラート: 改ざんの可能性が検出されました
ファイル権限
| ファイル | 権限 | 内容 |
|---|---|---|
~/.secretctl/ | 0700 | Vault ディレクトリ |
vault.db | 0600 | 暗号化シークレット、暗号化DEK、salt(vault_keys テーブル) |
vault.salt | 0600 | レガシー Argon2 salt(128ビット)、DB に移行 |
vault.meta | 0600 | バージョン、タイムスタンプ |
audit/ | 0700 | 監査ログディレクトリ |
audit/*.jsonl | 0600 | 月次 HMAC チェーン監査レコード |
audit/audit.meta | 0600 | チェーン状態メタデータ |
mcp-policy.yaml | 0600 | MCP アクセスポリシー |
サプライチェーンセキュリティ
最小限の依存関係
secretctl は以下のみを使用:
- Go 標準ライブラリ
golang.org/x/crypto(Argon2, HKDF)modernc.org/sqlite(ストレージ、Pure Go 実装)github.com/spf13/cobra(CLI フレームワーク)
検証
# バイナリチェックサムを検証
sha256sum secretctl-darwin-arm64
# 依存関係をチェック
go list -m all
既知の制限
メモリ保護
Go のガベージコレクターがメモリ割り当てを管理。これは:
- シークレットがガベージコレクションまでメモリに残る可能性
- 確実なシークレットのゼロ化は不可能
軽減策: secretctl はシークレットの存在時間とスコープを最小化。
対象外の脅威
業界標準(Vault、1Password、Infisical)と一貫して:
- Root/カーネルレベルの侵害
- 物理デバイスアクセス(アンロック時)
- 弱いマスターパスワード
- メモリダンプ攻撃
詳細は脅威モデルを参照。
バージョン履歴
| バージョン | セキュリティ変更 |
|---|---|
| v0.8.x | マルチフィールドシークレット、フィールド感度、パスワード変更 |
| v0.7.x | AI安全設計用語、secret_get_masked 強化 |
| v0.6.x | secret_run のバインディングサポート |
| v0.5.x | 監査ログ HMAC チェーン、出力サニタイズ |
| v0.4.x | MCP サーバー、AI安全設計実装 |
| v0.3.x | 初期 Argon2id + AES-256-GCM 実装 |
関連ドキュメント
- 脅威モデル - ビジュアル脅威モデル
- 仕組み - 技術アーキテクチャ
- 暗号化詳細 - 暗号仕様
- MCP ツールリファレンス - AI 連携セキュリティ
セキュリティ連絡先
脆弱性を見つけましたか?責任を持って報告してください:
- メール: secretctl.oss@gmail.com
- 公開の GitHub Issue を開かないでください
- SECURITY.md を参照