はじめに
Claude Codeには「Skills」という仕組みがあります。特定用途のノウハウをSKILL.mdというMarkdownファイルにまとめておき、Claudeが必要な時だけ読み込んで動作を切り替える機能です。
本記事では、実際にセキュリティレビュー用のskill(security-review)を設計した過程を題材に、SKILL.mdの文法(守らないと壊れる部分)とベストプラクティス(守らなくても動くが、質が落ちる部分)を切り分けて整理します。
Skillsの基本構造
Skillsは3層構造で読み込まれます。
1. メタデータ(name + description) … 常にコンテキストに存在
2. SKILL.md本文 … skillが発火した時だけ読み込まれる
3. 補助リソース(references/等) … 本文から参照された時だけ読み込まれる
この段階的開示の仕組みを理解しておくと、「どの情報をどの層に置くべきか」の判断がしやすくなります。
文法(Syntax)- 守らないと壊れる部分
frontmatter(YAML)
---
name: skill-name
description: いつ使うか、何をするかを書く
---
-
name:必須です。/name でスキルを実行します。 -
description:必須です。そのスキルが「何をするものか」「いつ使うべきか」をClaudeに伝えるための説明です。
YAML構文としての基本ルール(コロンの後の半角スペース、インデントの一貫性)を守らないとパースに失敗します。
本文(Markdown)
ここには厳密な文法は存在しません。見出し構成も箇条書きも表も自由に組めます。「文法違反で壊れる」のはfrontmatterのYAML部分だけで、本文側は次章のベストプラクティスの領域になります。
ベストプラクティス - 守らなくても動くが、質が落ちる部分
1. 500行を目安に収める
本文が肥大化する場合はreferences/配下に外出しし、SKILL.mdからは「必要な時にこのファイルを読む」という導線だけを書きます。300行を超えるreferenceファイルには目次を付けます。
2. description記述のコツ
トリガー条件は具体的なファイル名・状況で書きます。
❌ 抽象的:セキュリティに関する時に使う
◯ 具体的:SecurityConfig、AuthController、Filter系クラスを扱う際は必ず使う
抽象的な条件は「発火しない/誤発火する」の両方のリスクを生みます。
3. 複数ドメインを持つskillはファイル分割する
.claude/
└── skills/
├── SKILL.md
└── references/
├── aws.md
├── gcp.md
└── azure.md
Claudeは必要なreferenceファイルだけを読み込む設計にできます。
設計:security-review skillのケーススタディ
ここからは「Spring BootアプリケーションをOWASP Top 10観点でレビューするskill」を題材に、「なぜこの構成にしたか」を残します。
全体構成
1. frontmatter(name / description)
2. 宣言文 + スコープ方針 + 発火方針
3. レビュー対象の優先順位
4. チェック観点表
5. 出力フォーマット
6. 進め方
実際のSKILL.md全文
そのままコピーして.claude/skills/security-review/SKILL.mdに配置すれば動作します。
---
name: security-review
description: Spring Boot(Java)アプリケーションのコードをOWASP Top 10観点でレビューし、根拠付きの指摘レポートを作成する。「セキュリティレビューして」「OWASPの観点でチェックして」「脆弱性がないか見て」といった依頼、または認証・認可・セッション管理・CORS・ログ出力を含むコード(SecurityConfig、AuthController、Filter系クラス等)を扱う際は必ずこのスキルを使うこと。
---
# セキュリティレビュー(OWASP Top 10観点)
Spring Bootのコードに対して、OWASP Top 10の**全10項目**と照合し、指摘には必ず「該当ファイル・該当箇所」「なぜそう判断したか」「確認すべき次の質問」をセットで示す。単なる一般論の羅列にしない。
**スコープ方針**:本skillはOWASP Top10全項目を対象とする。ただしA06(脆弱で古いコンポーネント)の詳細な依存関係スキャンは`dependency-check`等の専用ツール領域であり、本skillでは「バージョン確認の要否」を指摘するに留め、実スキャンは代替しない。
**発火方針**:意図的に狭く設計している。「セキュリティレビューして」等の明示的な依頼、または認証・認可系コードを直接編集対象とする場合のみ発火する。通常のコードレビュー・PRレビュー全般には自動適用しない(レビューコスト・時間がかかるため、不要な場面での発火は避ける)。
## レビュー対象の優先順位
以下のファイル・機能があれば優先的に読む:
1. **SecurityConfig系**(`SecurityFilterChain`, `authorizeHttpRequests`)→ アクセス制御・CSRF・CORS設定
2. **AuthController系**(ログイン・ログアウト処理)→ 認証方式、パスワード検証、セッション/トークン発行
3. **Filter/Interceptor系**(Request/ResponseのログやTracing)→ 機密情報のログ出力有無
4. **application.yml / application-*.yml**→ 認証情報のハードコード有無
5. **PasswordEncoder関連**→ ハッシュ化方式・強度
## チェック観点(OWASP Top 10ベース)
| 観点 | 確認すること | Spring Bootでの典型パターン |
|---|---|---|
| A01 アクセス制御の不備 | ロールごとのエンドポイント制御が漏れなく閉じているか。最後が`denyAll()`等でデフォルト拒否になっているか | `authorizeHttpRequests`のルール順序・網羅性 |
| A02 暗号化の不備 | パスワードが平文/弱いハッシュで保存されていないか。機密情報が設定ファイルに直書きされていないか | `BCryptPasswordEncoder`の有無・強度(10以上推奨) |
| A03 インジェクション | SQLを文字列結合していないか(JPA/Repositoryのメソッド定義を優先しているか) | `@Query`のネイティブSQLでの結合有無 |
| A04 安全でない設計 | 価格・数量など、サーバー側で計算すべき値をクライアントから直接受け取っていないか。入力バリデーションがDTOレベルで機能しているか | `@Valid`, `@Min`/`@Max`等のBean Validationアノテーションの有無。Order/Product系DTOでの金額・数量の扱い |
| A05 セキュリティ設定のミス | エラーレスポンスが内部情報(スタックトレース等)を露出していないか | `exceptionHandling`のハンドラー実装 |
| A06 脆弱で古いコンポーネント | 依存ライブラリに既知の脆弱性を持つ古いバージョンが含まれていないか(本skillでは要否の指摘に留め、実スキャンはしない) | `build.gradle.kts`の依存バージョン。Dependabot/OWASP Dependency-Check等の導入有無 |
| A07 識別と認証の失敗 | ログイン試行回数の制限(ブルートフォース対策)があるか | Controllerにレート制限の実装があるか |
| A08 ソフトウェアとデータの整合性の不具合 | 信頼できないデータのデシリアライズを許容していないか。CI/CDパイプラインの取得元が正規リポジトリに限定されているか | Jacksonのポリモーフィックデシリアライズ設定、依存取得元のリポジトリ設定 |
| A09 セキュリティログ・監視の不備 | 認証失敗・アクセス拒否などのセキュリティイベントがログに記録されているか | `AuthenticationEntryPoint`/`AccessDeniedHandler`内でのログ出力有無 |
| A10 SSRF | サーバー側から外部URLへリクエストする機能で、ユーザー入力のURLをそのまま使用していないか | `RestTemplate`/`WebClient`呼び出し箇所でのURL検証有無 |
| CSRF対策(OWASP外・Spring Boot固有) | セッションベース認証でCSRF保護を無効化している場合、代替対策(SameSite Cookie等)があるか | `.csrf(AbstractHttpConfigurer::disable)`の有無とセッション管理方式の組み合わせ |
| CORS設定(OWASP外・Spring Boot固有) | `allowCredentials(true)`と`allowedOrigins`の組み合わせが妥当か(本番で許可オリジンが限定されているか) | `CorsConfigurationSource`の設定内容 |
| ログでの機密情報露出(A09の補足) | リクエストボディ(パスワード等)をそのままログ出力していないか | Filter/Interceptorのログ出力項目 |
## 出力フォーマット
必ず以下の3列構成の表で出力する:
| OWASP項目 | 評価(◎良好 / ⚠️要注意 / ❌問題あり) | 根拠(ファイル名・該当コード・理由) |
「⚠️要注意」「❌問題あり」の項目には、根拠に続けて **「確認すべき次の質問」** を1文添える(例:「本番の許可オリジンリストが限定的か要確認」)。
「◎良好」の項目も削らずに残す。良い設計判断を明示することは、レビューの信頼性を上げ、後から見返す際に「意図してそうなっているか」を追跡しやすくする。
## 進め方
1. 対象リポジトリ/ディレクトリから優先順位の高いファイルを実際に読む(推測でレビューしない)
2. 上記チェック観点表に沿って照合し、該当なしの項目は表から省く(無理に全項目を埋めない)
3. 出力フォーマットの表を提示
4. 「⚠️要注意」項目があれば、それを潰すための次のアクション(設定変更・追加実装・確認事項のヒアリング)を1〜2個提案する
発火方針を本文の上位に明記した理由
このskillは「セキュリティレビューして」等の明示的な依頼、または認証・認可系コードを直接編集する場合のみ発火するよう、意図的に狭く設計しました。理由は、通常のコードレビュー全般に自動適用するとレビューコストが増え、不要な場面での発火を招くためです。
スコープ方針を明記した理由
OWASP Top 10は全10項目(A01〜A10)を対象とすると決めましたが、A06(脆弱で古いコンポーネント)については実際の依存関係スキャンまでは行わず、「バージョン確認の要否を指摘するに留める」と役割の境界を明記しました。
これは、専用ツール(Dependabot、OWASP Dependency-Check等)が担うべき領域とskillが担う領域を混同しないための線引きです。線引きを書かずに省略すると、後から見た人が「なぜA06だけ扱いが薄いのか」を判断できなくなります。
出力フォーマットを観点表の直後に置いた理由
「何を見るか(観点表)」と「どう返すか(出力フォーマット)」を隣接させることで、表の各行がそのまま出力の各行に対応する1対1の関係を視覚的に示しています。進め方(手順)は最後に独立させ、「材料と評価基準が揃った後にどう動くか」という実行フェーズを分離しました。
まとめ
- 文法として守る必要があるのはfrontmatterのYAML部分のみです。
- 本文は自由なMarkdownですが、ベストプラクティス(500行目安・段階的開示・description記述のコツ等)を守ると再現性が上がります
- 発火方針・スコープ方針・並び順は、書いた理由ごと本文に残すことで、後から見返した時に検証・説明可能な状態を保てます