detecting-secrets について
detecting-secretsは、コードレビュー担当者がハードコードされた認証情報を探し、テスト値、プレースホルダー、暗号化データと実際の秘密情報を見分けるためのセキュリティスキルです。漏えい後の認証情報のローテーション、コードの除去、再発防止の手順も示します。
主な機能
- 01キー、パスワード、接続文字列の検出
- 02コードや設定内の見落としやすい場所の確認
- 03文脈に基づく誤検知の判定
- 04認証情報のローテーションとコード除去
- 05GitHubアラートとコミット履歴の確認
活用シーン
- 01コード内のハードコード認証情報の監査
- 02テストデータやプレースホルダーの誤検知確認
- 03認証情報漏えい後のローテーションと除去の計画
- 04GitHub Secret Scanningアラートの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bitwarden/ ai-plugins --skill detecting-secretsインストール後、Claude Code で/detecting-secretsを入力して読み込まれたか確認します。
詳細
シークレットのパターン
コードにハードコードされたシークレットがないか、以下のカテゴリを確認します。
信頼度の高いパターン
| 種類 | パターン例 |
|---|---|
| API キー | AKIA[0-9A-Z]{16}(AWS)、AIza[0-9A-Za-z_-]{35}(Google)、*apiKey*、*api_key* という名前の変数に代入された文字列 |
| 接続文字列 | Server=...;Password=...、mongodb://user:pass@host、postgres://user:pass@host |
| 秘密鍵 | -----BEGIN RSA PRIVATE KEY-----、-----BEGIN OPENSSH PRIVATE KEY----- |
| トークン | ghp_[A-Za-z0-9]{36}(GitHub PAT)、xoxb-(Slack ボット)、sk-(OpenAI) |
| パスワード | *password*、*passwd*、*secret*、*credential* という名前の変数に代入された値 |
| 証明書 | パスワードが埋め込まれた PFX/P12 ファイル、秘密鍵を含む PEM ファイル |
信頼度の低いパターン(コンテキストの確認が必要)
- 設定内の Base64 エンコード文字列(暗号化されている場合も、平文のシークレットの場合もあります)
- JWT トークン(テスト用トークンの場合も、本番用トークンの場合もあります)
- 32 文字以上の 16 進文字列(暗号化キーの場合も、ハッシュの場合もあります)
- 認証情報が埋め込まれた URL(
https://user:pass@host)
コンテキストを考慮した検出
実際のシークレットと誤検出を区別します。パターンに一致しても、必ずしも実際のシークレットとは限りません。コンテキストを考慮してください。
テストフィクスチャとモックデータ
// NOT a real secret — test fixture with obvious fake value
var testApiKey = "test-api-key-not-real-12345";
var mockPassword = "P@ssword123"; // Used only in unit tests
// REAL secret — production-looking value in non-test code
var apiKey = "sk-proj-abc123def456ghi789jkl012mno345pqr678stu901vwx";
判断基準:
- テストディレクトリ(
**/test/**、**/tests/**、**/*.Test/**)内にありますか? - 値に明らかなプレースホルダーテキスト(「test」、「fake」、「mock」、「example」、「placeholder」)が含まれていますか?
- 値はアサーションまたはモックの設定で使用されていますか?
例示値とプレースホルダー値
// NOT a real secret — documented example
{
"apiKey": "YOUR_API_KEY_HERE"
}
// REAL secret — actual value in config
{
"apiKey": "sk-proj-abc123def456ghi789jkl012mno345pqr678stu901vwx"
}
暗号化またはハッシュ化された値
- ハッシュ化されたパスワード(bcrypt
$2b$、argon2$argon2id$)はシークレットではなく、適切に保存されています - 適切なキー管理のもとで暗号化された値は、同じ意味でのシークレットではありません
- ただし、暗号化キー自体がハードコードされている場合、それはシークレットです
よく隠されている場所
シークレットを監査する際は、以下の場所を検索します。
| 場所 | 確認する内容 |
|---|---|
appsettings.json / appsettings.Development.json | 接続文字列、API キー、サービス認証情報 |
.env / .env.local | 実際の値が設定された環境変数の定義 |
web.config / app.config | マシンキー、接続文字列 |
docker-compose.yml / Dockerfile | 認証情報を含む ENV ディレクティブ、シークレットを含むビルド引数 |
CI/CD ファイル(.github/workflows/*.yml) | ${{ secrets.* }} 参照の代わりにインラインで記述されたシークレット |
| テスト用シードスクリプト / マイグレーションファイル | データベースのパスワード、サービスアカウントの認証情報 |
| コメントと TODO メモ | コメントに残された「一時的な」認証情報 |
| デフォルトのパラメーター値 | function connect(password = "admin123") |
| 定数ファイル | 一元管理された認証情報の定義 |
GitHub シークレットスキャンとの連携
# List all secret scanning alerts
gh api /repos/{owner}/{repo}/secret-scanning/alerts --jq '.[] | {number, state, secret_type, secret_type_display_name, created_at, push_protection_bypassed}'
# Get details for a specific alert
gh api /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}
# List alerts that bypassed push protection
gh api "/repos/{owner}/{repo}/secret-scanning/alerts?state=open" --jq '.[] | select(.push_protection_bypassed == true)'
プッシュ保護は、検出されたシークレットを含むコミットのプッシュを防ぎます。プッシュ保護を回避した場合は、アラートが通知されます。これらは特に慎重に確認してください。
修復ワークフロー
コード内でシークレットが見つかった場合は、次の手順に従ってください。
1. 直ちにローテーションする
コミットされたシークレットは漏えいしたものと見なしてください。リポジトリが非公開であっても、シークレットがキャッシュされたり、ログに記録されたり、CI/CD システムからアクセスされたりしている可能性があります。
- 既存の認証情報を無効にする
- 新しい認証情報を生成する
- 使用しているすべての場所(サービス、デプロイ)で認証情報を更新する
2. コードから削除する
ハードコードされたシークレットを、安全な参照に置き換えてください。
// WRONG — hardcoded secret
var connectionString = "Server=prod.db;Password=s3cr3t!";
// CORRECT — environment variable
var connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
// CORRECT — Azure Key Vault (Bitwarden's approach)
var connectionString = await keyVaultClient.GetSecretAsync("db-connection-string");
3. Git の履歴から削除する(必要な場合)
シークレットが公開リポジトリ、または今後公開されるリポジトリにコミットされていた場合:
# Using git filter-repo (preferred over filter-branch)
git filter-repo --path-glob '*.json' --replace-text expressions.txt
# expressions.txt format:
# literal:the-secret-value==>REDACTED
警告: Git の履歴を書き換えると元に戻せず、すべてのコラボレーターに影響します。シークレットが公開リポジトリ、または近く公開されるリポジトリで漏えいした場合にのみ実施してください。
4. 再発を防止する
- コミットしてはならないファイル(
.env、*.pfx、appsettings.Development.json)を.gitignoreに追加する - リポジトリで GitHub のプッシュ保護を有効にする
- 組織固有のシークレット形式には、シークレットスキャンのカスタムパターンを使用する
安全な代替手段
Bitwarden では、BRE チームがプロビジョニングする Azure Key Vault をシークレット管理に使用しています。
| 代わりに使用するもの | 使用するもの |
|---|---|
| ハードコードされた接続文字列 | Azure Key Vault のシークレット |
| 設定ファイル内の API キー | デプロイ時に設定する環境変数 |
| ソース内の証明書 | Azure Key Vault の証明書 |
| コード内で共有するチームの認証情報 | マネージド ID(Azure) |
| CI/CD ワークフローファイル内のシークレット | GitHub Actions のシークレット(${{ secrets.NAME }}) |
ローカル開発では、user-secrets または .gitignore に登録した .env ファイルを使用してください。決してコミットしないでください。
重要なルール
- コミットされたシークレットは漏えいしたものと見なしてください。 リポジトリが非公開であっても、必ずローテーションしてください。例外はありません。
- ローテーションせずにシークレットスキャンのアラートを無効化しないでください。 アラートを却下しても、漏えいがなくなるわけではありません。
- 検出だけでなく検証も行ってください。 シークレットの可能性がある値が見つかった場合は、アラートを上げる前に本物かどうかを確認してください。テスト値、プレースホルダー、暗号化されたコンテンツではないか確認します。
- コミット履歴全体を確認してください。 最新のコミットで削除されたシークレットが、Git の履歴に残っている場合があります。
git log -p -S "secret-pattern"を使用して履歴を検索してください。 - シークレット管理には Bitwarden で Azure Key Vault を使用しています。新しいシークレットを保存する必要がある場合は、リポジトリで Vault を利用できるよう、BRE と連携してアクセスをプロビジョニングしてください。