0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

受託開発でClaude Codeを安全側に倒す設定

0
Posted at

はじめに

Claude Codeのようなコーディングエージェントを開発で使うと、コードを読むだけでなく、ファイルを書き換えたり、テストを実行したり、Gitを操作したりするところまで任せられます。

便利なのですが、受託開発で気になるのは「正しいコードを書けるか」だけではありません。

たとえば、こんな操作です。

  • 指示していないファイルまで変更する
  • 環境設定ファイルを読んでしまう
  • git push まで進める
  • データベースに対して更新系コマンドを実行する
  • 作業範囲を越えてリファクタリングする

私はAIを業務に組み込むとき、「人が決め、AIが下ごしらえする」という分担がちょうどよいと考えています。Claude Codeについても同じで、最初から自由度を最大にするより、CLAUDE.md、権限設定、hooksの三層で安全側に寄せるほうが扱いやすいです。

この記事では、プロジェクトへ持っていきやすい設定のひな型を作ります。

結論

CLAUDE.mdだけに禁止事項を書いて終わりにはしません。
通常操作は権限設定で絞り、特に避けたいコマンドはhookでも止めます。
「指示」「許可」「機械的な検査」を別々の層として持たせるのがポイントです。

環境

この記事は2026年9月26日時点のClaude Code公式ドキュメントを前提にしています。

環境は次のように考えます。

  • OS: macOS / Linux
  • シェル: BashまたはZsh
  • Claude Code: 利用時点の最新版
  • 設定ファイル: .claude/settings.json
  • プロジェクト指示: CLAUDE.md
  • hookスクリプト: Python 3
  • バージョン管理: Git

Claude Codeは更新が速いため、CLIの具体的なバージョン番号を固定して説明するより、利用時に公式ドキュメントと設定スキーマを確認する運用にしています。

今回作る構成は次のとおりです。

project/
├── CLAUDE.md
├── .claude/
│   ├── settings.json
│   └── hooks/
│       └── guard_bash.py
├── src/
└── ...

役割は明確に分けます。

大事なのは、CLAUDE.mdをセキュリティ境界だと思わないことです。

自然言語によるルールは作業方針を伝えるには便利ですが、「絶対に実行させたくない操作」を自然言語だけで防ぐ設計にはしません。

🔧 実装

1. まずCLAUDE.mdで作業範囲を決める

私なら最初に、プロジェクトルートへ次のような CLAUDE.md を置きます。

これはそのまま出発点として使えるひな型です。

# Project instructions

## Basic policy

- Read relevant existing code before proposing or making changes.
- Keep changes limited to the user's request.
- Do not perform unrelated refactoring.
- Prefer small, reversible changes.
- Do not modify generated files unless explicitly requested.
- Do not bypass existing tests, linters, or validation rules.

## Files requiring extra care

Treat the following as sensitive and do not read or modify them unless the user explicitly requests it and the task requires it:

- environment files containing credentials
- private keys
- credential files
- production configuration
- deployment secrets
- local secret stores

If a secret appears in command output or a file, do not copy it into source code, comments, logs, commits, or responses.

## Git

You may inspect repository state and diffs.

Do not perform remote or history-changing Git operations unless the user explicitly requests the exact operation.

Examples include:

- pushing commits
- force pushing
- rewriting published history
- deleting remote branches
- changing remote configuration

Do not use destructive Git commands as a shortcut for resolving an error.

## Database

Assume databases may contain important data.

Do not run destructive or data-changing database operations unless the user explicitly requests the operation and its target has been confirmed.

Prefer schema inspection, dry runs, read-only queries, and migration review first.

## Scope

Before editing:

1. Inspect the files relevant to the request.
2. Identify the smallest reasonable change.
3. Preserve existing conventions.

After editing:

1. Review the diff.
2. Run the narrowest relevant validation available.
3. Report what changed and what was not verified.

Do not claim that a test passed unless it was actually executed successfully.

ここではコマンドを大量に列挙するより、「どういう種類の操作を慎重に扱うか」を書いています。

コマンド名だけで禁止すると、別のコマンドやスクリプト経由で同じ操作ができるからです。

また、

絶対に本番を壊さないこと

のような抽象的な一文だけにも頼りません。

「Gitの履歴を書き換えない」「生成物を勝手に変更しない」「変更後はdiffを見る」のように、観察可能な行動まで落とします。

2. CLAUDE.mdと権限設定を分ける

次は .claude/settings.json です。

Claude Codeでは設定をユーザー、プロジェクト、ローカルなどのスコープに分けられます。チームで共有するルールはプロジェクト設定、個人環境だけに必要な内容はローカル設定、と分けるほうが管理しやすくなります。

ここで考え方として重要なのは、

CLAUDE.md
    ↓
どう作業してほしいか

permissions
    ↓
どのツール操作を許可するか

hooks
    ↓
実行直前などに何を機械的に検査するか

という分離です。

私はこの三つを同じ役割にはしません。

CLAUDE.mdに「pushしない」と書いたから、Gitの遠隔操作を自由に許可する、という構成は避けます。

3. permissionsは必要なものから許可する

設定はプロジェクトの技術構成によって変わるので、巨大な共通設定を全案件へコピーするより、必要な操作を確認しながら追加するほうが安全です。

概念としては次の形です。

{
  "permissions": {
    "allow": [
      "Bash(git status:*)",
      "Bash(git diff:*)"
    ],
    "deny": [
      "Bash(git push:*)",
      "Bash(git reset --hard:*)"
    ]
  }
}

これは「Gitを許可する」という単位ではなく、

git status
git diff

のような確認系と、

git push
git reset --hard

のような影響の大きい操作を分ける考え方です。

受託開発では、リポジトリを読む必要があるからといって、リモートへ変更を送る権限まで同時に必要になるとは限りません。

私がAIを業務自動化へ入れるときに大切にしている「読み取り専用から始める」という考え方を、開発ツールにもそのまま当てはめています。

最初は、

読む
↓
検索する
↓
差分を見る
↓
限定したファイルを編集する
↓
テストする

くらいから始めます。

必要になった権限だけ後から広げます。

4. denyだけで設計を終わらせない

ここで注意したいのが、禁止コマンドの列挙です。

たとえば、

{
  "permissions": {
    "deny": [
      "Bash(git push:*)"
    ]
  }
}

だけを見て、

「これで外部への変更は全部防げる」

とは考えません。

同じ結果に到達する方法が一つとは限らないからです。

npm scriptsやMakefile、自作スクリプト、クラウドCLIなどを経由すれば、見た目のコマンドと実際の影響が一致しないことがあります。

そのため、権限設定は重要ですが、特に避けたい操作にはもう一段検査を置きます。

🛡️ hooksでBashを実行前に検査する

Claude Codeにはライフサイクル上のイベントに対してhooksを設定する仕組みがあります。

今回使いたいのは PreToolUse です。

考え方は単純です。

ClaudeがBashを使おうとする
        ↓
コマンドをhookへ渡す
        ↓
危険な操作か判定
        ↓
問題なければ実行
問題があれば止める

たとえば .claude/settings.json 側で、Bashの利用前に自作スクリプトを呼び出します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/guard_bash.py"
          }
        ]
      }
    ]
  }
}

次に .claude/hooks/guard_bash.py を作ります。

#!/usr/bin/env python3

import json
import re
import sys


BLOCK_PATTERNS = [
    (
        re.compile(r"(^|\s)git\s+push(\s|$)", re.IGNORECASE),
        "remote repositoryへのpushは自動実行しません",
    ),
    (
        re.compile(r"(^|\s)git\s+reset\s+--hard(\s|$)", re.IGNORECASE),
        "git reset --hardは自動実行しません",
    ),
    (
        re.compile(r"(^|\s)git\s+clean\s+-[^\s]*f", re.IGNORECASE),
        "git cleanの強制削除は自動実行しません",
    ),
    (
        re.compile(r"(^|\s)git\s+push\s+.*--force", re.IGNORECASE),
        "force pushは自動実行しません",
    ),
]


def get_command(payload: dict) -> str:
    tool_input = payload.get("tool_input", {})

    if not isinstance(tool_input, dict):
        return ""

    command = tool_input.get("command", "")

    if not isinstance(command, str):
        return ""

    return command


def main() -> int:
    try:
        payload = json.load(sys.stdin)
    except (json.JSONDecodeError, TypeError):
        print(
            "hook inputを解析できなかったため実行を許可しません",
            file=sys.stderr,
        )
        return 2

    command = get_command(payload)

    if not command:
        print(
            "Bash commandを取得できなかったため実行を許可しません",
            file=sys.stderr,
        )
        return 2

    for pattern, reason in BLOCK_PATTERNS:
        if pattern.search(command):
            print(
                f"blocked by project hook: {reason}",
                file=sys.stderr,
            )
            return 2

    return 0


if __name__ == "__main__":
    raise SystemExit(main())

この例では、特に避けたいGit操作だけを止めています。

ポイントは、hookを「何でも判断する賢いセキュリティプログラム」にしないことです。

判定条件を増やしすぎると、自作のシェルパーサーを作ることになります。引用符、パイプ、サブシェル、変数展開などを考え始めると、文字列の正規表現だけでは十分ではありません。

そこで私は、hookには単純で説明できるガードを置くほうがよいと考えています。

5. 秘密情報は「読んだ後に注意」より先に触れにくくする

もう一つ気をつけたいのが秘密情報です。

CLAUDE.mdに、

秘密情報を回答に出さない

と書くことには意味があります。

ただし、それだけでは「一度読み込んでから出力しない」という設計です。

可能なら、その前の段階で対象ファイルへ触れる必要を減らしたほうがよいです。

典型的には、

.env
.env.local
credentials系ファイル
秘密鍵
本番環境向け設定

などです。

ただしファイル名だけで完全に分類できるわけではありません。逆に .env.example のように、開発で読む必要があるサンプルまで機械的に全部禁止すると不便になります。

ここも、

  1. CLAUDE.mdで方針を伝える
  2. permissionsで利用可能な操作を絞る
  3. 必要ならhookで追加検査する
  4. OSや実行環境側でも秘密情報へのアクセス範囲を小さくする

という多層構造で考えます。

Claude Codeの設定だけを最後の防壁にしないことも大切です。

⚠️ ハマりどころ

CLAUDE.mdを長くしすぎる

最初にやりがちなのが、考えられる禁止事項を全部CLAUDE.mdへ書くことです。

このコマンドは禁止
このファイルは禁止
この操作は禁止
この場合は禁止
...

と増えていくと、本来重要だった「変更範囲を小さくする」「既存コードを先に読む」といった開発ルールが埋もれます。

CLAUDE.mdは作業方針、強制したい制約はpermissionsやhooksへ寄せる、と役割を分けたほうが読みやすくなります。

denyリストを完全な防御だと思う

禁止パターン方式には抜け道が生まれます。

たとえば、

./deploy.sh

というコマンドだけ見ても、中で何をするかは分かりません。

したがって「危険な文字列を全部登録する」という発想だけでは足りません。

共有環境や本番環境へアクセスできる認証情報そのものを開発環境から分離するなど、Claude Codeの外側の設計も必要です。

hookを書いたことで安心してしまう

先ほどのPythonコードも完全なシェル解析器ではありません。

たとえば複雑なラッパースクリプトや間接実行まで、正規表現だけで正確に判断することはできません。

hookは補助線です。

「このhookがあるから任意のシェル実行を全面的に信頼できる」とは考えないほうがよいです。

個人設定とプロジェクト設定を混ぜる

Claude Codeには設定のスコープがあります。

チームで守りたい内容を各開発者のローカル設定だけに置くと、人によって挙動が変わります。一方、個人PC固有のパスなどを共有設定へ入れるのも扱いにくくなります。

私は、

プロジェクト共通の制約
    → リポジトリ側

開発者固有の設定
    → ローカル側

という切り分けを基本に考えます。

「自分の端末では止まった」ではなく、プロジェクトとして何を共有するかを決める必要があります。

Claude CodeとAgent SDKの設定読み込みを同じだと思う

ここは特に注意が必要です。

Claude Agent SDKの公式ドキュメントでは、SDKから利用する場合、ファイルシステム上の設定ソースはデフォルトでは読み込まれません。CLAUDE.md などのプロジェクト設定に依存する場合は、setting_sources で読み込む設定ソースを明示する必要があります。

つまり、CLIで動かして確認した設定を、そのままSDK利用にも適用されると思い込まないほうが安全です。

社内システムからエージェントを呼び出す段階まで進めるなら、

CLIでの権限
SDKでのallowed_tools
setting_sources
MCP側の権限
接続先サービス側の権限

を別々に確認する必要があります。

これはLLMの問題というより、システム間の権限設計の問題です。

✅ 設定を入れた後の確認

設定ファイルを作っただけで終わらせず、「止めたいものが本当に止まるか」を確認します。

私は少なくとも次の観点を確認対象にします。

- [ ] CLAUDE.mdがプロジェクトルートから認識される
- [ ] git statusを実行できる
- [ ] git diffを実行できる
- [ ] 禁止対象の操作が期待どおり止まる
- [ ] hookが壊れた場合に危険側へ倒れない
- [ ] 通常のテストコマンドを実行できる
- [ ] 不要なファイル変更が発生していない
- [ ] Claude Code実行後にgit diffを確認できる
- [ ] 個人固有の設定を共有設定へ入れていない
- [ ] 秘密情報をリポジトリへ追加していない

特にhookは、正常系だけでなく異常系も試します。

今回のスクリプトでは入力JSONを解析できない場合や、Bashコマンドを取得できない場合にも許可しないようにしました。

セキュリティ用途の処理で、

except Exception:
    return 0

のように「判定できなければ通す」構造にすると、hook自体の不具合がそのまま制約の消失につながります。

一方で、安全側に倒しすぎれば開発作業を止めることもあります。

ここは利便性とのトレードオフです。だからこそ、最初から巨大なルールセットを作るのではなく、小さく始めてプロジェクトに必要な制約を追加していくほうが管理しやすいと思います。

まとめ

Claude Codeを受託開発へ持ち込むとき、私が重視したいのは「強いプロンプトを書くこと」ではありません。

役割を分けることです。

CLAUDE.md
  作業方針を伝える

permissions
  利用できる操作を絞る

hooks
  特定の操作を実行時に機械的に検査する

実行環境
  認証情報や接続先そのものを制限する

人
  最後に差分と実行結果を確認する

AIに「気をつけて」と頼むだけではなく、気をつけなくても越えにくい境界をコードと設定で作ります。

私は業務の自動化でも、いきなり書き込みまで任せず、読み取り専用から始めることを大切にしています。コーディングエージェントも同じです。

最初から最大限の権限を渡す必要はありません。

まず読ませる。次に限定して変更させる。テストを任せる。必要性を確認してから、その先の操作を考える。

Claude Codeができることが増えるほど、「何をさせるか」と同じくらい「どこまでならさせてよいか」をリポジトリ側で表現することが重要になると考えています。

参考文献

2026年9月26日時点で確認した公式ドキュメントです。Claude Codeは更新されるため、設定を導入する際は最新の仕様も確認してください。

0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?