LESSON 40分

ストーリー

高橋アーキテクト
ところで、APIキーやデータベースのパスワードはどこに保存している?

高橋アーキテクトがコードベースを検索しました。

高橋アーキテクト
まさか、.envファイルをGitにコミットしていないだろうな?
あなた
してないですけど….env.exampleにサンプル値が入っています
高橋アーキテクト
問題はそこだ。シークレットの管理は、多くのプロジェクトが失敗するポイントだ。専用のシークレット管理ツールを使う方法を学ぼう

シークレットとは

シークレットとは、漏洩するとシステムのセキュリティが損なわれる機密情報のことです。

種類
認証情報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分