「.envファイルにAPIキーをハードコードしてGitにコミットしてしまった」——SES現場で一度は聞いたことがあるインシデントではないでしょうか。AWS Secrets Managerを正しく活用すれば、このようなシークレット漏洩リスクをゼロにできます。
AWS Secrets Managerは、APIキー・データベースパスワード・OAuth トークンなどのシークレットを暗号化して一元管理し、自動ローテーション機能でセキュリティを継続的に維持するフルマネージドサービスです。この記事では、AWSシリーズ第40弾として、Secrets Managerの実践的な活用法をSES現場の視点で解説します。
この記事を3秒でまとめると
- AWS Secrets Managerでシークレットを暗号化一元管理し、ハードコード・.env漏洩を完全防止
- 自動ローテーションでDBパスワード・APIキーを定期更新し、漏洩時の影響範囲を最小化
- Lambda・ECS・EKSとのシームレスな連携パターンで、SES現場のあらゆるアーキテクチャに対応
AWS Secrets Managerとは
概要と位置づけ
AWS Secrets Managerは、アプリケーションで使用する機密情報(シークレット)を安全に管理するためのサービスです。
管理できるシークレットの種類:
- データベース認証情報(RDS, Aurora, Redshift)
- APIキー・アクセストークン
- OAuth 2.0 クライアントシークレット
- SSH鍵・TLS証明書
- 任意のキー・バリューペア
Systems Manager Parameter Store との違い
| 比較項目 | Secrets Manager | Parameter Store (SecureString) |
|---|---|---|
| 自動ローテーション | ✅ ネイティブ対応 | ❌ カスタム実装が必要 |
| 料金 | $0.40/シークレット/月 | 無料(Standard)/ $0.05(Advanced) |
| クロスアカウント共有 | ✅ リソースポリシーで可能 | ❌ 直接共有不可 |
| RDS連携 | ✅ 自動ローテーション | ❌ 手動 |
| バージョン管理 | ✅ ステージング対応 | ✅ バージョンあり |
| 最大サイズ | 64KB | 8KB (Standard) / 8KB (Advanced) |
使い分けの判断基準:
- 自動ローテーションが必要 → Secrets Manager
- DB認証情報 → Secrets Manager(RDSとの統合が優秀)
- コスト重視・設定値中心 → Parameter Store
- クロスアカウント共有 → Secrets Manager
基本操作
シークレットの作成
AWS CLIでの作成:
# RDSデータベースの認証情報を登録
aws secretsmanager create-secret \
--name "prod/myapp/database" \
--description "本番環境のRDS認証情報" \
--secret-string '{"username":"admin","password":"P@ssw0rd!2026","host":"mydb.cluster-xxxxx.ap-northeast-1.rds.amazonaws.com","port":"5432","dbname":"myapp"}' \
--tags Key=Environment,Value=production Key=Team,Value=backend
# APIキーの登録
aws secretsmanager create-secret \
--name "prod/myapp/stripe-api-key" \
--description "Stripe APIキー" \
--secret-string '{"api_key":"sk_live_xxxxx","webhook_secret":"whsec_xxxxx"}'
CloudFormationでの作成:
Resources:
DatabaseSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: prod/myapp/database
Description: 本番環境のRDS認証情報
GenerateSecretString:
SecretStringTemplate: '{"username": "admin"}'
GenerateStringKey: password
PasswordLength: 32
ExcludeCharacters: '"@/\'
Tags:
- Key: Environment
Value: production
シークレットの取得
AWS SDK(Node.js):
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManagerClient({ region: 'ap-northeast-1' });
async function getSecret(secretName) {
const command = new GetSecretValueCommand({ SecretId: secretName });
const response = await client.send(command);
return JSON.parse(response.SecretString);
}
// 使用例
const dbConfig = await getSecret('prod/myapp/database');
const pool = new Pool({
host: dbConfig.host,
port: dbConfig.port,
user: dbConfig.username,
password: dbConfig.password,
database: dbConfig.dbname,
});
AWS SDK(Python):
import json
import boto3
def get_secret(secret_name: str) -> dict:
client = boto3.client('secretsmanager', region_name='ap-northeast-1')
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
# 使用例
db_config = get_secret('prod/myapp/database')

自動ローテーションの設定
RDSパスワードの自動ローテーション
Secrets Managerの最大の強みは、DBパスワードの自動ローテーションです。
# ローテーション設定(30日ごと)
aws secretsmanager rotate-secret \
--secret-id "prod/myapp/database" \
--rotation-lambda-arn "arn:aws:lambda:ap-northeast-1:123456789012:function:SecretsManagerRotation" \
--rotation-rules '{"AutomaticallyAfterDays": 30}'
CloudFormationでの設定:
Resources:
SecretRotationSchedule:
Type: AWS::SecretsManager::RotationSchedule
Properties:
SecretId: !Ref DatabaseSecret
RotateImmediatelyOnUpdate: true
RotationLambdaARN: !GetAtt RotationFunction.Arn
RotationRules:
AutomaticallyAfterDays: 30
# シークレットとRDSの紐付け
SecretTargetAttachment:
Type: AWS::SecretsManager::SecretTargetAttachment
Properties:
SecretId: !Ref DatabaseSecret
TargetId: !Ref MyRDSInstance
TargetType: AWS::RDS::DBInstance
ローテーション戦略の選択
Single User Rotation(シンプル):
- 1つのDBユーザーのパスワードを更新
- ローテーション中に一瞬だけ接続が切れる可能性
- 小規模・開発環境向け
Alternating Users Rotation(推奨):
- 2つのDBユーザーを交互に使用
- ダウンタイムゼロでローテーション可能
- 本番環境向け
# Alternating Users Rotation の設定
GenerateSecretString:
SecretStringTemplate: !Sub '{"username": "app_user_1", "masterarn": "${MasterSecret}"}'
GenerateStringKey: password
PasswordLength: 32
カスタムローテーション関数
API キーなど、AWS管理外のシークレットにも自動ローテーションを設定できます。
# Lambda: カスタムローテーション関数
import boto3
import json
import requests
def lambda_handler(event, context):
secret_id = event['SecretId']
step = event['Step']
token = event['ClientRequestToken']
client = boto3.client('secretsmanager')
if step == 'createSecret':
# 新しいAPIキーを外部サービスから生成
new_key = requests.post('https://api.example.com/keys/rotate').json()
client.put_secret_value(
SecretId=secret_id,
ClientRequestToken=token,
SecretString=json.dumps({'api_key': new_key['key']}),
VersionStages=['AWSPENDING']
)
elif step == 'setSecret':
# 外部サービス側で新しいキーをアクティベート
pending = get_secret_version(client, secret_id, 'AWSPENDING')
requests.post('https://api.example.com/keys/activate',
json={'key': pending['api_key']})
elif step == 'testSecret':
# 新しいキーの動作確認
pending = get_secret_version(client, secret_id, 'AWSPENDING')
response = requests.get('https://api.example.com/health',
headers={'Authorization': f'Bearer {pending["api_key"]}'})
if response.status_code != 200:
raise ValueError('新しいAPIキーの検証に失敗')
elif step == 'finishSecret':
# ローテーション完了
client.update_secret_version_stage(
SecretId=secret_id,
VersionStage='AWSCURRENT',
MoveToVersionId=token,
RemoveFromVersionId=get_current_version(client, secret_id)
)
アプリケーションとの連携パターン
パターン1: Lambda関数での利用
// Lambda + Secrets Manager
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
// Lambda実行環境でのキャッシュ(コールドスタート対策)
let cachedSecret = null;
let cacheExpiry = 0;
const CACHE_TTL = 300000; // 5分
async function getSecretCached(secretName) {
if (cachedSecret && Date.now() < cacheExpiry) {
return cachedSecret;
}
const client = new SecretsManagerClient({});
const response = await client.send(
new GetSecretValueCommand({ SecretId: secretName })
);
cachedSecret = JSON.parse(response.SecretString);
cacheExpiry = Date.now() + CACHE_TTL;
return cachedSecret;
}
export const handler = async (event) => {
const dbConfig = await getSecretCached('prod/myapp/database');
// DB接続して処理実行
};
パターン2: ECSタスクでの利用
{
"containerDefinitions": [
{
"name": "myapp",
"image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest",
"secrets": [
{
"name": "DB_HOST",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prod/myapp/database:host::"
},
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prod/myapp/database:password::"
},
{
"name": "STRIPE_API_KEY",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prod/myapp/stripe-api-key:api_key::"
}
]
}
],
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole"
}
パターン3: EKSでの利用(External Secrets Operator)
# ExternalSecret リソース
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: myapp-secrets
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/myapp/database
property: password
- secretKey: API_KEY
remoteRef:
key: prod/myapp/stripe-api-key
property: api_key
EKSガイドで、Kubernetesとの連携を詳しく解説しています。
IAMポリシーとアクセス制御
最小権限の原則に基づくポリシー設計
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadSpecificSecrets",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": [
"arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prod/myapp/*"
],
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "ap-northeast-1"
}
}
}
]
}
リソースポリシーによるクロスアカウント共有
マルチアカウント構成で、本番アカウントのシークレットを他アカウントから参照できます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::987654321098:role/AppRole"
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*",
"Condition": {
"StringEquals": {
"secretsmanager:ResourceTag/SharedWith": "staging-account"
}
}
}
]
}
コスト最適化
料金体系
| 項目 | 料金(東京リージョン) |
|---|---|
| シークレット保管 | $0.40/シークレット/月 |
| API呼び出し | $0.05/10,000回 |
| 自動ローテーション | 追加料金なし(Lambda実行料金のみ) |
コスト削減のベストプラクティス
1. キャッシュの活用 頻繁にアクセスするシークレットはアプリケーション側でキャッシュし、API呼び出し回数を削減します。
// AWS公式のキャッシュライブラリを使用
import { getSecretValue } from '@aws-sdk/client-secrets-manager';
// 5分間キャッシュ(ローテーション間隔より十分短く設定)
const CACHE_TTL_MS = 300_000;
2. 不要なシークレットの削除 使用されていないシークレットを定期的に棚卸しします。
# 90日以上アクセスされていないシークレットを検出
aws secretsmanager list-secrets \
--query 'SecretList[?LastAccessedDate < `2025-12-21`].{Name:Name,LastAccess:LastAccessedDate}'
3. Parameter Store との使い分け シークレットではない設定値は、Parameter Store(無料枠)に保管します。
コスト最適化ガイドで、AWS全体のコスト管理を解説しています。
SES現場での実践パターン
パターン1: マルチ環境でのシークレット管理
SES案件では、開発・ステージング・本番の複数環境を管理することが一般的です。
## 命名規則
{environment}/{service}/{secret-type}
例:
- dev/user-service/database
- stg/user-service/database
- prod/user-service/database
- prod/user-service/stripe-api-key
- prod/payment-service/database
- shared/infra/github-token
パターン2: 既存プロジェクトへのSecrets Manager導入
ハードコードされたシークレットをSecrets Managerに移行するステップ:
Step 1: シークレットの洗い出し
# コードベース内のシークレット候補を検出
grep -rn "password\|api_key\|secret\|token" --include="*.js" --include="*.py" --include="*.yml" .
Step 2: Secrets Manager にシークレットを登録
Step 3: アプリケーションコードの修正
Step 4: .envファイル・設定ファイルからシークレットを削除
Step 5: CI/CDパイプラインの更新
パターン3: セキュリティ監査への対応
# CloudTrailでシークレットへのアクセスログを確認
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::SecretsManager::Secret \
--max-results 50
IAMセキュリティガイドで、アクセス制御の詳細を解説しています。
まとめ:Secrets Managerでシークレット漏洩リスクをゼロにする
AWS Secrets Managerを導入することで、シークレットのハードコード・.env漏洩のリスクを完全に排除できます。
導入ステップ:
- 既存のハードコードされたシークレットを洗い出し、Secrets Managerに登録
- アプリケーションコードをSDK経由での取得に修正
- RDS認証情報の自動ローテーションを設定
- IAMポリシーで最小権限のアクセス制御を実装
- CloudTrailでアクセスログを監査
**シークレット管理はセキュリティの基本です。**SES案件でSecrets Managerを使いこなせるエンジニアは、セキュリティ意識の高さを評価され、信頼を得られます。
SES BASEでは、AWS・セキュリティ領域のSES案件を多数掲載しています。案件を検索するからチェックしてみてください。