ストーリー
高橋アーキテクトがコードベースを検索しました。
シークレットとは
シークレットとは、漏洩するとシステムのセキュリティが損なわれる機密情報のことです。
| 種類 | 例 |
|---|---|
| 認証情報 | DBパスワード、APIキー、OAuthクライアントシークレット |
| 暗号化キー | AES鍵、JWTシークレット |
| 証明書 | TLS証明書、mTLS証明書 |
| トークン | アクセストークン、サービスアカウントキー |
| 接続文字列 | データベースURL、Redis URL |
アンチパターン:やってはいけないこと
// アンチパターン1: ソースコードにハードコード
const API_KEY = "sk-1234567890abcdef"; // 絶対にダメ
// アンチパターン2: 環境変数を.envファイルでGit管理
// .env がGitにコミットされている
// アンチパターン3: 設定ファイルに平文で保存
// config.json: { "db_password": "password123" }
// アンチパターン4: ログにシークレットを出力
console.log(`Connecting with password: ${DB_PASSWORD}`);
// アンチパターン5: エラーメッセージにシークレットを含める
throw new Error(`Failed to connect: ${connectionString}`);
シークレット管理ツール
1. HashiCorp Vault
// HashiCorp Vault からシークレットを取得
import Vault from "node-vault";
const vault = Vault({
apiVersion: "v1",
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN,
});
const getSecret = async (path: string): Promise<Record<string, string>> => {
const result = await vault.read(`secret/data/${path}`);
return result.data.data;
};
// 使用例
const startApp = async () => {
const dbSecrets = await getSecret("myapp/database");
const dbConfig = {
host: dbSecrets.host,
port: parseInt(dbSecrets.port),
username: dbSecrets.username,
password: dbSecrets.password,
database: dbSecrets.database,
};
// シークレットを環境変数に設定しない(メモリ内のみ)
const db = new Pool(dbConfig);
};
2. AWS Secrets Manager
import {
SecretsManagerClient,
GetSecretValueCommand,
} from "@aws-sdk/client-secrets-manager";
const client = new SecretsManagerClient({ region: "ap-northeast-1" });
const getSecret = async (secretId: string): Promise<Record<string, string>> => {
const command = new GetSecretValueCommand({ SecretId: secretId });
const response = await client.send(command);
if (response.SecretString) {
return JSON.parse(response.SecretString);
}
throw new Error("Secret not found");
};
// シークレットのキャッシュ(頻繁なAPI呼び出しを避ける)
class SecretCache {
private cache = new Map<string, { value: Record<string, string>; expiresAt: number }>();
private ttl = 5 * 60 * 1000; // 5分
async get(secretId: string): Promise<Record<string, string>> {
const cached = this.cache.get(secretId);
if (cached && cached.expiresAt > Date.now()) {
return cached.value;
}
const value = await getSecret(secretId);
this.cache.set(secretId, {
value,
expiresAt: Date.now() + this.ttl,
});
return value;
}
}
シークレットのローテーション
// シークレットの自動ローテーション
interface RotationConfig {
secretId: string;
rotationInterval: number; // 日数
rotationFunction: () => Promise<string>; // 新しいシークレットを生成
}
const rotateSecret = async (config: RotationConfig): Promise<void> => {
// 1. 新しいシークレットを生成
const newSecret = await config.rotationFunction();
// 2. 新しいシークレットでテスト接続
const testResult = await testConnection(config.secretId, newSecret);
if (!testResult.success) {
throw new Error("New secret validation failed");
}
// 3. シークレットストアを更新
await secretManager.updateSecret(config.secretId, newSecret);
// 4. 古いシークレットを一定期間保持(ロールバック用)
await secretManager.markPrevious(config.secretId);
// 5. 監査ログに記録
await auditLog.record({
action: "secret_rotation",
secretId: config.secretId,
timestamp: new Date(),
});
};
シークレット管理のベストプラクティス
| プラクティス | 説明 |
|---|---|
| 集中管理 | 全シークレットを1つのツールで管理 |
| 暗号化保存 | シークレットは常に暗号化して保存 |
| アクセス制御 | 最小権限でシークレットへのアクセスを制限 |
| 監査ログ | シークレットへのアクセスを全て記録 |
| ローテーション | 定期的にシークレットを更新 |
| 短命トークン | 長期間有効なトークンを避ける |
| Git除外 | .gitignoreに.envを含める |
| スキャン | CIでシークレットの混入を検出 |
// CIでのシークレット検出(pre-commit hook)
// .gitleaks.toml の設定例
const gitleaksConfig = {
rules: [
{
description: "AWS Access Key",
regex: "AKIA[0-9A-Z]{16}",
tags: ["aws", "key"],
},
{
description: "Generic API Key",
regex: "(?i)(api[_-]?key|apikey)\\s*[=:]\\s*['\"][a-zA-Z0-9]{20,}['\"]",
tags: ["api", "key"],
},
{
description: "JWT Secret",
regex: "(?i)(jwt[_-]?secret)\\s*[=:]\\s*['\"][^'\"]{10,}['\"]",
tags: ["jwt", "secret"],
},
],
};
まとめ
| ポイント | 内容 |
|---|---|
| シークレット | APIキー、パスワード等の機密情報 |
| 管理ツール | Vault、AWS Secrets Manager で集中管理 |
| ローテーション | 定期的に更新して漏洩リスクを低減 |
| 検出 | CIパイプラインでシークレットの混入を検出 |
チェックリスト
- シークレットの種類と危険性を理解した
- Vault / AWS Secrets Manager の基本的な使い方を把握した
- シークレットローテーションの重要性を理解した
- Git へのシークレット混入防止策を把握した
次のステップへ
次は「通信の保護」を学びます。TLS、mTLS、証明書管理について理解し、通信経路全体のセキュリティを設計しましょう。
推定読了時間: 40分