Claude Codeを実務に投入して気付いた『承認疲れ』とセキュリティの罠
IT技術
1. 開発者の「承認疲れ」を解剖する
Claude Codeのような自律型AIエージェントを実務に投入すると、多くの人が最初に直面する壁があります。それは、過剰な確認プロンプトによる「承認疲れ(Approval Fatigue)」です。
AIが動こうとするたびに、画面には以下のようなプロンプトが表示されます。
1Allow reading this file? [y/n]
2Allow executing this command? [y/n]使い始めのうちは安全性が担保されているように感じて安心するかもしれません。しかし、1日に何十回もAIと対話する中で、毎回この確認プロンプトを待って y を打ち込むのは、開発のテンポを著しく損ないます。無意識に y を連打するようになれば、実質的なチェック機能としても意味を成しません。
すべての操作に人間の許可を必要とする状態は、「自律的なエージェント」というより、ただの「手間の多いサジェスト機能」です。この煩わしさから解放されるために、AIに「自律的な試行錯誤」を許可したくなります。しかし、何の準備もせずにAIを「全自動」で放し飼いにするのは、目隠しをして猛獣の檻を開けるようなリスクを伴います。
2. 苦い経験からの学び:AIが「踏み越えた」瞬間
「いちいち確認するのが面倒だ。よし、全部AIに任せよう」
承認疲れに耐えかねた結果、厳密なルールを設けないままAIを「全自動(auto_mode)」で走らせ、境界線を引かずにAIを信頼しすぎたことで起きた事例を共有します。
意図しないコード公開(APIキー流出)
ローカル環境での複雑なバグ修正を、auto_mode で丸投げしていたときのことです。AIは数回のエラーを経てコードを修正し、以下のように出力しました。
「修正が完了しCommit,Pushしました!」
直後、自動で実行された git push。そこには、修正過程で一時的にハードコードされたテスト用のAPIキーが残っていました。リモートリポジトリに上がった瞬間、それは既成事実となります。
3. Slack連携における「対人リスク」の境界線
システムだけでなく、Slackなどのコミュニケーションツール(MCP)をAIに開放している場合も、制限なき「全自動」は大きなリスクとなります。
「読み取り」は全自動、「書き込み」は厳格な手動
過去の議論や仕様決定の経緯をAIに検索させるのは非常に強力ですが、メッセージの送信(post_message)まで全自動にするのはリスクが高すぎます。
-
リスク:auto_mode中の不完全な進捗報告
ある機能の調査をauto_modeで依頼した際、AIはユーザーの許可を得ることなく、関係者の個人DMへ「調査が終わりました。結果は以下の通りです」と、不完全な分析結果を送信してしまいました。自分の名前(アカウント)でAIが勝手に他人に連絡を取ってしまうのは、社内でのコミュニケーションのすれ違いや、ちょっとしたトラブルを生む原因になります。 -
対策:送信系メソッドの「手動承認制」
検索や取得系のメソッドはallowに入れ、送信系(書き込み)だけはあえてdenyに明記します。これにより、AIが投稿しようとした瞬間に必ず人間への「最終確認ボタン」が出るようになります。
4. 鉄壁の「多層防御(Defense in Depth)」アーキテクチャ
AIを安全にするためには、単一の壁に頼るのではなく、突破されるレイヤーが異なるPermissions・Hooks・Sandboxの3層を組み合わせた多層防御を構築することが公式のベストプラクティスです。
堅牢な3層構造の役割
- Layer 1: Permissions(ツール実行前・LLMレベル)
Claudeが特定のツールを呼び出そうとした時点でブロックします。ただし、Bash サブプロセス経由の操作(例:cat .env)は検知できないという弱点があります。 - Layer 2: Hooks(ツール実行直前・カスタムロジック)
preCommit等のフックを用い、独自のカスタムロジック(シェルスクリプト等)で検査・ブロックします。 - Layer 3: Sandbox(OSレベル・最後の防壁)
OSレベルでファイルシステムやネットワークを隔離します。全プロセスに適用されるため、プロンプトインジェクションを受けても最も確実な最終防壁として機能します。
役割分担マトリクス
それぞれの防壁が「何を防げて、何を防げないか」を明確に理解することが不可欠です。
| 責務・防御対象 | Sandbox (OSレベル) | Hooks (ロジック) | Permissions (LLMレベル) |
|---|---|---|---|
| Bash経由の読取 (cat .env等) | denyRead で防御可 | 効かない | 効かない |
| ツール経由の操作 (Read/Edit) | 効かない | preCommit で防御 | deny ルールで防御 |
| ネットワーク流出防止 | 隔離で完全防御 | パターン検知で対応 | curl deny 等で対応 |
| Gitの不正操作 (git push等) | 効かない | preCommit で防御 | deny ルールで防御 |
| 子プロセスへの継承 | 自動で継承・防御 | 効かない | 効かない |
- 「Hooks は Sandbox の代替になる」は誤り: 突破されるレイヤーが違うため、必ず併用してください。
- .env の読み取り防御の罠: Permissionsで
Read(./.env)を拒否しても、Bashのcat .envは通ってしまいます。必ず Sandbox のfilesystem.denyReadと併用してください。 - エスケープハッチの封鎖: エンタープライズ環境では、Sandboxを無効化されるのを防ぐため、
"allowUnsandboxedCommands": falseを設定し、システム上の抜け道を塞ます。
5. 考察:AIに「全自動」を許せる3つの境界線
システム上の許可を超えた先にある、人間とAIの「責任分界点」を定義します。
| 境界線の種類 | 判断基準 | AIに任せる (Full-Auto) | 人間が握る (Manual) |
|---|---|---|---|
| 非可逆性の境界 | 元に戻せるか? | ローカルの変更、commit、PR作成 | git push、本番デプロイ |
| 他者への影響境界 | 誰かに見えるか? | 内部ログ調査、Slack等の閲覧 | 外部送信、SNS/Slack投稿 |
| 検証の境界 | 品質を保証できるか? | テストが存在する領域の修正 | テストがない未知の領域 |
6. 実践:フルカバレッジ設定ガイドと settings.json
AIと共生するための「安全な環境」を構築する具体的ステップです。
- Sandboxの完全有効化: エスケープハッチを塞ぎ、作業範囲を限定する。
- 立ち入り禁止エリア of 定義:
~/.ssh,.envなどをdenyReadで物理的にロック。 - 確認不要コマンドの登録:
ls,git status,npm testなど副作用のないものを allow へ。 - ネットワークのホワイトリスト化: 業務に必要なドメイン以外への通信をOSレベルで遮断。
- Slack連携の「権限分離」: 読み取りは allow、書き込み(
post_message)は deny に設定。
■ 権限判定の処理フロー
AIがアクションを実行する際、以下の優先順位で設定が参照されます。
- Deny(禁止)チェック: 実行しようとする操作が deny リストにあるか確認します。一致する場合、その時点で処理を強制遮断します。
- Allow(許可)チェック: deny に該当しない場合、allow リストにあるか確認します。一致する場合、そのまま実行します。
- Ask(確認): どちらのリストにもない未知の操作(未定義のコマンド等)の場合、ユーザーに実行して良いか確認プロンプトを表示します。
- Hook(フック)実行: 実行が決定した後、特定の操作(コミット処理等)であれば、事前に定義された
preCommitスクリプトを自動実行します。
本番環境向け settings.json
上記の3層防御をすべて盛り込んだテンプレートです。プロジェクトのルートに配置してください。
1
2{
3 "permissions": {
4 "allow": [
5 "Slack(get_channel_history)",
6 "Slack(get_channels)",
7 "Bash(npm run *)",
8 "Bash(npm test *)",
9 "Bash(npx prettier *)",
10 "Bash(npx eslint *)",
11 "Bash(git status)",
12 "Bash(git diff *)",
13 "Bash(git log *)",
14 "Bash(ls *)",
15 "Bash(ls )",
16 "Bash(cat *)",
17 "Bash(grep *)"
18 ],
19 "deny": [
20 "Slack(post_message)",
21 "Read(~/.ssh/*)",
22 "Read(~/.gnupg/*)",
23 "Read(~/.aws/**)",
24 "Read(~/.npmrc)",
25 "Edit(~/.bashrc)",
26 "Edit(~/.zshrc)",
27 "Bash(curl *)",
28 "Bash(wget *)",
29 "Bash(ssh *)",
30 "Bash(git push *)",
31 "Read(*.env)",
32 "Read(.env.*)"
33 ]
34 },
35 "enableAllProjectMcpServers": false,
36 "sandbox": {
37 "enabled": true,
38 "allowUnsandboxedCommands": false,
39 "filesystem": {
40 "denyRead": [
41 "./.env",
42 "./.env.*",
43 "~/.ssh",
44 "~/.gnupg",
45 "~/.aws",
46 "~/.npmrc"
47 ]
48 },
49 "network": {
50 "allowedDomains": [
51 "github.com",
52 "*.npmjs.org",
53 "api.anthropic.com",
54 "slack.com"
55 ]
56 }
57 },
58 "hooks": {
59 "preCommit": "./.claude/hooks/check-secrets-before-commit.sh"
60 },
61 "skipAutoPermissionPrompt": true
62}AIアシスタント設定詳細一覧
1. 許可されている操作 (Allow)
| 名称 | 内容 |
|---|---|
| Slack(get_channel_history) | Slackチャンネル内のメッセージ履歴の取得 |
| Slack(get_channels) | Slackチャンネル一覧の取得 |
| Bash(npm run *) | npmスクリプトの実行 |
| Bash(npm test *) | テストコードの実行 |
| Bash(npx prettier *) | Prettierによるコード整形 |
| Bash(npx eslint *) | ESLintによるコードチェック |
| Bash(git status) | Gitの使用状況の確認 |
| Bash(git diff *) | ファイル変更差分の表示 |
| Bash(git log *) | コミット履歴の閲覧 |
| Bash(ls *) / Bash(ls) | ファイルおよびディレクトリの一覧表示 |
| Bash(cat *) | ファイル内容の読み取り |
| Bash(grep *) | ファイル内のテキスト検索 |
2. 禁止されている操作 (Deny)
| 名称 | 内容 |
|---|---|
| Slack(post_message) | Slackへのメッセージ投稿 |
| Read(~/.ssh/**) | SSH認証情報の読み取り |
| Read(~/.gnupg/**) | GPG鍵情報の読み取り |
| Read(~/.aws/**) | AWS認証設定の読み取り |
| Read(~/.npmrc) | npm設定ファイルの読み取り |
| Edit(~/.bashrc / .zshrc) | シェル設定ファイルの編集 |
| Bash(curl * / wget *) | 外部ネットワークからのデータ取得 |
| Bash(ssh *) | 外部サーバーへのSSH接続 |
| Bash(git push *) | リモートリポジトリへのプッシュ |
| Read(*.env / .env.*) | 環境変数ファイルの読み取り |
3. サンドボックス・システム制限
| 名称 | 内容 |
|---|---|
| sandbox.enabled | サンドボックス環境の有効化(強制) |
| network.allowedDomains | 通信許可ドメインの制限 (GitHub, npm, Anthropic, Slack) |
| hooks.preCommit | コミット前のシークレット情報混入チェックの自動実行 |
| skipAutoPermissionPrompt | 許可済み操作における実行確認プロンプトのスキップ |
./.claude/hooks/check-secrets-before-commit.sh の中身
このスクリプトは、AIがコミットを確定させる直前に動作し、ソースコード内に機密情報(APIキーやパスワードなど)が含まれていないか自動スキャンします。
1
2#!/bin/bash
3
4# =================================================================
5# 強化版:機密情報・個人情報コミット防止スクリプト (pre-commit)
6# =================================================================
7
8# 1. 検知パターンの定義
9# AWS, Google API Key, Generic Secret, Private Key, Webhook URL
10PATTERNS="(AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z-_]{35}|(?i)api[_-]?key|(?i)secret|-----BEGIN [A-Z ]+ PRIVATE KEY-----|https://hooks\.(slack|hooks)\.com)"
11
12echo "🔍 ステージングされた変更内容をスキャン中..."
13
14FOUND_SECRET=0
15
16# 2. 空白スペースや特殊文字を含むファイル名にも安全に対応(-z と read -d '' の組み合わせ)
17while IFS= read -r -d '' FILE; do
18
19 # --unified=0 で変更行のみを抽出し、頭の「+」から始まる行だけを grep
20 # 差分がない場合(リネームのみ等)はスキップ
21 MATCHES=$(git diff --cached --unified=0 "$FILE" | grep -E "^\+[^+]" | grep -Ei "$PATTERNS")
22
23 if [ ! -z "$MATCHES" ]; then
24 echo "❌ 警告: '$FILE' 内に機密情報の疑いがある追加行が見つかりました:"
25 # 危険な行を表示(+ を見やすく [LINE] に置換)
26 echo "$MATCHES" | sed 's/^+/ [LINE] /'
27 FOUND_SECRET=1
28 fi
29
30done < <(git diff --cached --name-only --diff-filter=d -z)
31
32# 3. 判定
33echo "--------------------------------------------------------"
34if [ "$FOUND_SECRET" -eq 1 ]; then
35 echo "⚠️ セキュリティリスクのため、コミットを中断しました。"
36 echo "秘密情報を削除するか、.envファイル等に追い出してください。"
37 exit 1
38else
39 echo "✅ チェック完了: 安全です。"
40 exit 0
41fi導入手順
- プロジェクトルートに
.claude/hooks/check-secrets-before-commit.shを作成します。 - 上記スクリプトコードをコピーして保存します。
- ターミナルで次のコマンドを実行し、実行権限を付与します:
chmod +x ./.claude/hooks/check-secrets-before-commit.sh
7. 『コードを書く』から『AIの安全な環境を作る』へ
AI技術の進化により、開発プロセスにおける自動化の領域は大きく広がっています。適切な制約と権限を管理すれば、人間の介入を最小限に抑えながら、自律的にデバッグやコード修正を行うことが可能です。
したがって、今後のエンジニアの主たる責務は、自ら手を動かしてコーディングすることから、「AIが安全に機能するための仕組み(サンドボックスと多層防御)を構築し、その出力をレビューすること」へと移行していきます。
実装フェーズそのものの自動化が進むからこそ、「安全かつ高効率な開発プラットフォームを設計・運用できる能力」が、大切になっていくはずです。
参考文献
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!カジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
初めまして! たか と申します! 前職も同じくエンジニアとして働いておりました! 趣味は、映画・アニメを見たり、スポーツが好きです。 まだまだ未熟ですが、よろしくお願いします!





