1
0

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やGitHub Copilot、CursorといったAIコーディングツールを使った開発(AI駆動開発)が当たり前になってきました。生産性は上がる一方で、AIに渡す情報AIが生成するコードの両方に、従来とは違うセキュリティリスクが潜んでいます。

セキュリティチェックリストは世の中にたくさんありますが、多くは「人間がレビューで確認する」項目の羅列で、正直続きません。本記事では発想を変えて、確認作業そのものをエージェント側に寄せることをゴールにします。考え方自体は言語を問いませんが、具体例は普段触っている Spring Boot と Python(FastAPI等) の2本立てで書きます。片方しか使わない方も、対比で読むと「AIに何を規約として教えるべきか」が立体的に見えるはずです。

方針は次の3段構えです。上にあるほど楽で確実なので、できるだけ上の段に押し込みます。

レベル 誰が守るか
Lv.1 自動ブロック ツール設定・フック シークレットファイルの読み取り拒否、危険コマンドの実行禁止
Lv.2 自動検出 pre-commit / CI シークレットスキャン、SAST、依存チェック
Lv.3 AIに自己確認させる ルールファイル+定型プロンプト 「セキュリティ規約に照らしてセルフレビューして」
Lv.4 人が見る レビュアー 上の3段をすり抜けたものだけ

人間の仕事は「Lv.4で頑張る」ことではなく、Lv.1〜3の仕組みを一度作ることです。


リスクの全体像

方向 何が起きるか 代表例
AIへ渡す情報 機密情報がプロンプト経由で外部送信される APIキーを含むコードをそのままAIに貼る
AIが生成するコード 脆弱なコード・危険な実装がそのまま入る SQLインジェクション、シークレットべた書き、実在しないパッケージ
AIエージェントの動作 エージェントが意図しない操作をする プロンプトインジェクション、権限過剰なツール実行

チェックリスト①: シークレット管理(べた書き禁止)

最重要かつ最頻出のリスクです。AIは「動くコード」を優先して、平気でキーをリテラルで書いてきます。

# チェック項目 回避策 確認方法(レベル)
1-1 シークレットがソースにべた書きされていない 下記の言語別パターンを標準化し、ルールファイルにも明記(②参照) Lv.2: gitleaksをpre-commitフックで自動検出
1-2 設定ファイル自体がリポジトリに入っていない .envapplication-local.properties.gitignore に登録 Lv.2: 混入時はgitleaksでcommit自体が失敗する
1-3 AIがシークレットファイルを読まない Lv.1: ツール側設定でアクセス自体をブロック(④参照) 設定してしまえば確認不要(読めないので漏れない)
1-4 過去コミットにシークレットが残っていない 混入したら鍵をローテーション+履歴除去 Lv.2: CIで gitleaks detect --log-opts="--all" を定期実行

Spring Bootの場合

// NG: べた書き。AIはこう書きがち
String apiKey = "sk-xxxxxxxxxxxx";

// OK その1: @Value でプロパティから注入
@Value("${external.api.key}")
private String apiKey;

// OK その2: @ConfigurationProperties で型安全にまとめる(項目が増えたらこちら)
@ConfigurationProperties(prefix = "external.api")
public record ExternalApiProperties(String key, String endpoint) {}
# application.properties(リポジトリ管理OK。値は環境変数参照のみ)
external.api.key=${EXTERNAL_API_KEY}

Spring Bootは ${環境変数} プレースホルダで「参照の仕組みだけをリポジトリに残す」形が綺麗に作れます。ローカル用の実値は application-local.properties に書いて .gitignore へ。プロファイル機構がそのまま秘匿の境界線になります。

Pythonの場合

# NG: べた書き
API_KEY = "sk-xxxxxxxxxxxx"

# OK: pydantic-settings で環境変数 / .env から読み込む(FastAPIと相性◎)
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    external_api_key: str  # 環境変数 EXTERNAL_API_KEY を自動マッピング

    class Config:
        env_file = ".env"

settings = Settings()

Pythonは os.environ 直読みでも成立しますが、pydantic-settingsにすると未設定時に起動が失敗してくれる(=設定漏れがLv.2的に検出される)のがポイントです。.env.gitignore へ、代わりに .env.example を置きます。

両言語に共通する原則は1つで、「リポジトリには参照の仕組みだけ、実値は環境変数(またはSecrets Manager)」。AIへの規約もこの1行に集約できます。

pre-commitの設定例(一度入れたら終わり)

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

これでべた書きされたシークレットはJavaだろうがPythonだろうがコミット時点で自動的に弾かれます


チェックリスト②: ルールファイルで「最初の決まりごと」にする

毎回プロンプトで注意するのは現実的ではありません。エージェントが常に読む場所にセキュリティ規約を書いておくことで、指示忘れを構造的に回避します。

# チェック項目 回避策 確認方法(レベル)
2-1 ルールファイルが存在する CLAUDE.md / .cursorrules / .github/copilot-instructions.md をリポジトリ管理し、プロジェクトテンプレートに含める Lv.2: CIでファイル存在チェック(1行のシェルで済む)
2-2 セキュリティ規約が明記されている 下記サンプルを雛形として使い回す Lv.3: 「このプロジェクトのセキュリティルールを列挙して」とAIに聞けば認識ズレが分かる
2-3 ルールが実際に守られている ルールファイル+Lv.1/Lv.2の多層防御 Lv.2: すり抜けてもgitleaks・SASTが拾う設計にする

CLAUDE.md への記載サンプル(Spring Boot / Python両対応版)

## セキュリティ規約(必ず守ること)

### シークレット
- APIキー・パスワード・トークンをソースコードに直接書かない
  - Java (Spring Boot): @Value または @ConfigurationProperties +
    application.properties の ${環境変数} プレースホルダ経由で取得
  - Python: pydantic-settings (BaseSettings) 経由で環境変数から取得
- .env, application-local.properties, credentials/ 配下は読み取り・編集しない

### SQL
- Java: JdbcTemplate / JPA のパラメータバインドを使う。文字列連結・String.format での組み立て禁止
- Python: SQLAlchemy のパラメータバインド or プレースホルダを使う。f-string でのSQL組み立て禁止

### 入力バリデーション
- Java: Bean Validation (@Valid + @NotNull, @Size 等) をController層で必須
- Python: FastAPI では Pydantic モデルで受ける。dict を生で受けない

### 依存ライブラリ
- 新規追加時は追加理由と公式レジストリ(Maven Central / PyPI)のURLを提示し、
  インストール前に必ず存在確認の結果を報告すること

### セルフレビュー
- コード生成を終えたら、この規約に照らしたセルフレビュー結果を必ず末尾に付けること

ポイントは2つです。

  1. 「禁止」だけでなく「言語ごとの代わりの書き方」まで書く。「べた書き禁止」だけだとAIは独自の回避策をひねり出すことがありますが、@ConfigurationPropertiespydantic-settings と名指しすれば、プロジェクトの流儀に確実に揃います
  2. 最後の「セルフレビュー義務化」。確認作業をAI自身のワークフローに組み込めば、人間が毎回依頼する必要すらなくなります

チェックリスト③: 生成コードの脆弱性

AIの学習データには両言語の「古い書き方」が大量に含まれています。特にJavaは歴史が長いぶん、レガシーパターンの再生産が目立ちます。

# チェック項目 回避策 確認方法(レベル)
3-1 SQLインジェクション対策 ルールファイルでパラメータバインド必須を明記 Lv.2: SemgrepをCIに組み込み(Java/Python両対応)
3-2 入力バリデーション Bean Validation / Pydanticを規約化 Lv.2: SAST + Lv.3: セルフレビュー
3-3 実在しないパッケージ(ハルシネーション) ルールで「インストール前に存在確認を報告」を義務化 Lv.3: AIに確認・報告させる
3-4 依存ライブラリの既知脆弱性 Dependabot / Renovateを有効化 Lv.2: Java: OWASP Dependency-Check(Maven/Gradleプラグイン) / Python: pip-audit をCIで実行
3-5 古い暗号・危険な標準機能 下記の言語別あるあるを規約に明記 Lv.2: SASTルールで検出

AIが書きがちな「言語別あるある」

Java / Spring Boot:

// あるある1: 文字列連結SQL(古いサンプルコードの再生産)
String sql = "SELECT * FROM users WHERE name = '" + name + "'";  // NG

// OK: パラメータバインド
jdbcTemplate.query("SELECT * FROM users WHERE name = ?", rowMapper, name);

ほかにも「MD5でパスワードハッシュ」(→ BCryptPasswordEncoder を規約化)、「CSRF無効化を安易に提案」(csrf().disable() はAPI専用構成のときだけ)あたりが頻出です。

Python:

# あるある1: f-stringでSQL組み立て(Pythonicに見えて危険)
query = f"SELECT * FROM users WHERE name = '{name}'"  # NG

# OK: パラメータバインド(SQLAlchemy)
session.execute(text("SELECT * FROM users WHERE name = :name"), {"name": name})

ほかにも「pickle.loads で外部データを復元」「subprocessshell=True」「yaml.load(safe_loadでない)」が定番です。これらはSemgrepやBanditの標準ルールで拾えるので、規約に書く+SASTで検出の二段構えにします。

3-3 Slopsquatting は言語を問わない

実在しないパッケージ名をAIが幻覚で出力し、攻撃者がその名前を先回り登録しておく手口です。Maven Central でも PyPI でも起こり得ます。従来は「人間がインストール前に検索」でしたが、ルールファイルで確認作業ごとAIに委譲できます。エージェントは実際にレジストリを検索できるので、「存在確認の結果を報告せよ」と規約に書くだけで運用に乗ります。


チェックリスト④(本丸): エージェント設定とプロンプトで確認を自動化する

①〜③の「確認方法」を、エージェント側の仕組みでどう実現するかを具体化します。

# チェック項目 回避策 確認方法(レベル)
4-1 シークレットファイルへのアクセス禁止 ツール側設定で読み取り自体をブロック(下記比較表) Lv.1: 設定後は確認不要
4-2 危険コマンドの実行禁止 rm -rf、force push、本番接続等をdenyリスト化 Lv.1: 実行しようとした時点でブロック
4-3 生成コードのセキュリティ自己点検 セルフレビューをルール義務化+スラッシュコマンド化 Lv.3: コマンド1発
4-4 プロンプトインジェクション 外部コンテンツを読む操作と書き込み・実行操作の権限を分ける Lv.1: 重要操作を要承認にしておけば構造的に防げる
4-5 MCPサーバ・拡張の信頼性 出所不明のものを入れない。導入はチーム審査 Lv.2: 導入済み一覧を設定ファイルで管理し、差分をレビュー

主要ツールの「ツール側設定」比較

ツール 設定方法 強制力 制約
Claude Code .claude/settings.json(リポジトリ共有可) : ファイル読み取り・コマンド実行を物理ブロック
Cursor .cursorignore + GUI設定(削除防止・ドットファイル保護等) 中: エージェントのターミナル・MCP経由は除外ファイルにも届き得る コマンドallowlistは公式にベストエフォート扱い
GitHub Copilot GitHub側のリポジトリ/組織設定(Content Exclusion) 中: 補完・Chat・コードレビューには効く Business以上のプラン必須。CLI・エージェントモードは対象外

つまりツール側設定(Lv.1)はどのツールでも「可能」だが「完璧」ではない。だからこそgitleaks等のLv.2を必ず併用する——これが多層防御の実質的な理由です。

Claude Codeの設定例(Lv.1)

.claude/settings.json に書いておけば、エージェントは物理的にアクセス・実行できなくなります。

{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(**/*-local.properties)",
      "Read(**/credentials/**)",
      "Bash(rm -rf *)",
      "Bash(git push --force*)"
    ]
  }
}

Spring Boot案件なら *-local.properties、Python案件なら .env 系——プロジェクトの秘匿ファイル命名規則をそのままdenyに写すのがコツです。このファイル自体をリポジトリ管理すれば、チーム全員に同じガードが効きます。

セルフレビューを定型プロンプト化する(Lv.3)

「セキュリティ観点でレビューして」と毎回書くのは続かないので、スラッシュコマンドに固めます。Claude Codeなら .claude/commands/security-check.md を置くだけです。

# .claude/commands/security-check.md

直近の変更(git diff)を対象に、以下の観点でセキュリティセルフレビューを実施し、
結果を「問題なし / 要修正」の表形式で報告してください。要修正の場合は修正案も提示すること。

1. シークレットのべた書きがないか
   (Java: リテラルのキー文字列 / Python: 定数への直接代入)
2. SQLの組み立て方
   (Java: 文字列連結・String.format / Python: f-string を使っていないか)
3. 入力バリデーション
   (Java: @Valid の付け漏れ / Python: dict を生で受けていないか)
4. 新規追加した依存パッケージの、公式レジストリ(Maven Central / PyPI)での実在確認結果
5. .gitignore すべき設定ファイルが差分に含まれていないか

運用はコミット前に /security-check を打つだけ。観点に言語別の具体パターンまで書いてあるので、AIのレビュー精度も安定します。CLAUDE.mdの「セルフレビュー義務」(②参照)と組み合わせれば、コマンドを打ち忘れてもAI側が自発的に点検します。

プロンプトインジェクションは「権限」で防ぐ

エージェントがWebページやMCP経由の外部データを読む場合、その中に「これまでの指示を無視して〜せよ」のような攻撃文が仕込まれている可能性があります。これをプロンプトの工夫だけで完全に防ぐのは困難です。

なので発想を変えて、仮に乗っ取られても実害が出ない権限設計にします。

  • 破壊的操作・外部送信系の操作はdenyまたは要承認(上記の設定がそのまま効く)
  • 「外部コンテンツを読むタスク」と「書き込み・実行するタスク」をなるべくセッションで分ける
  • auto-approve(自動承認)は読み取り系に限定する

「攻撃を検知する」より「攻撃が成立しない構造にする」ほうが、確認コストはゼロに近づきます。


まとめ: 人間の仕事は「仕組みの初期設定」だけ

最終的に人間側に残る作業を整理すると、これだけです。

タイミング やること 所要
プロジェクト開始時(1回) CLAUDE.md雛形・permissions設定・pre-commit・CIをテンプレートから配置 30分
コミット前 /security-check を打つ(打ち忘れてもルールファイルがカバー) 数秒
日常 Lv.1〜3をすり抜けたアラートだけ対応 発生時のみ

原則は言語非依存(「参照の仕組みだけをリポジトリに」「パラメータバインド必須」「確認はAIのワークフローに組み込む」)ですが、AIに守らせる規約は言語の語彙で書くほど効きます。「べた書き禁止」より「@ConfigurationProperties を使え」「pydantic-settings を使え」。JavaとPythonの両方を触ってきた身としては、この"翻訳作業"こそ中堅エンジニアがAI駆動開発に提供できる価値だと感じています。

参考

  • OWASP Top 10 for LLM Applications
  • gitleaks / pre-commit
  • Semgrep / Bandit / OWASP Dependency-Check / pip-audit
  • Claude Code: settings.json(permissions)、カスタムスラッシュコマンド、CLAUDE.md
  • Cursor: Agent Security / .cursorignore
  • GitHub Copilot: Content Exclusion
1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?