はじめましての方ははじめまして、そうでない方はこんにちは。社内では「じぇっと」と呼ばれています。なぜそう呼ばれているかは追々お伝えできればいいかなと思います。
1. きっかけ
最近、社内であるサービスがJWT(JSON Web Token、ユーザー情報などを安全にやり取りするためのトークン形式)を発行している実装を見る機会がありました。署名鍵そのものをどこにどう置いているかまでは見えなかったのですが、ふと「AWSでJWTの署名鍵を持つとしたら、どこで管理するんだろう」と気になりました。
調べてみると、選択肢は大きく2つに絞られます。
- Secrets Manager に秘密鍵を保存し、アプリケーションが取得してローカルで署名する
-
KMSの非対称鍵と
SignAPIを使い、KMS側に署名させる
どちらも「AWSで秘密情報を安全に扱う」ためのサービスですが、性格はかなり違います。そして、この2つのどちらが「正解」かは一概には言えません。セキュリティ要件、運用体制、署名の頻度、コスト感度——チームの事情によって最適解が変わる類いの選択です。
この記事では「どちらを選ぶべきか」を断定するのではなく、判断に使うべき軸を整理します。自分のチームの状況に当てはめて読んでもらえればと思います。
断っておくと、筆者はインフラを専門にしているエンジニアではなく、普段はWebアプリケーションを書いているエンジニアです。この記事は、あくまでそういう目線で調べてまとめた判断軸です。インフラやセキュリティを専門にしている方からすると当たり前すぎる話や、逆に見落としている観点もあるかもしれません。その前提で読んでもらえると助かります。
2. 前提整理 — そもそも役割が違う2つのサービス
まず、両者の設計思想の違いを押さえておきます。
- Secrets Manager: 汎用の「秘密情報の保管庫」。データベース(DB)のパスワードでもAPIキーでも、文字列やJSONをそのまま暗号化して保存し、必要なときに取り出す。中身が何であるかはSecrets Manager自身は関知しない。
-
KMS: 暗号鍵の運用基盤。対称鍵なら
Encrypt/Decrypt、非対称鍵ならSign/Verifyといった「暗号操作そのもの」をAPI越しに提供する。鍵の実体(秘密鍵material)を外に出さないことが設計の核。
これをJWTの署名鍵に当てはめると、フローの形が変わります。
Secrets Managerパターン: 秘密鍵をシークレットとして保存し、アプリが起動時などに取得してメモリ上に保持、リクエストのたびにローカルで署名します。
// 起動時に一度だけ取得してキャッシュ
const { SecretString } = await secretsManager.getSecretValue({
SecretId: "jwt-signing-key",
});
const privateKey = SecretString; // PEM形式の秘密鍵
// リクエストのたびにローカルで署名
const token = jwt.sign(payload, privateKey, {
algorithm: "RS256",
keyid: "2026-07-a",
});
KMSパターン: JWTのheader・payloadは自前で組み立て、署名部分だけKMSの Sign API に投げます。秘密鍵自体はアプリ側に一切渡ってきません。
// header.payload をbase64url化したものを署名対象にする
const signingInput = `${base64url(header)}.${base64url(payload)}`;
// KMSに署名を依頼(秘密鍵はKMSの外に出ない)
const { Signature } = await kms.sign({
KeyId: "arn:aws:kms:ap-northeast-1:xxxx:key/xxxx",
Message: sha256(signingInput),
MessageType: "DIGEST",
SigningAlgorithm: "RSASSA_PKCS1_V1_5_SHA_256",
});
const token = `${signingInput}.${base64url(Signature)}`;
この「署名がどこで実行されるか」の違いが、以降の5つの判断軸すべての土台になります。
3. 判断軸1: 鍵materialの露出・漏えいリスク
GetSecretValue は、暗号化された秘密値を復号し、平文でシークレットの文字列やバイナリーデータを呼び出し元に返します。取得した瞬間から、鍵の実体は完全にアプリケーション側の管理下に置かれます。メモリダンプやログへの誤出力といった経路での漏えいリスクは、アプリ側の実装・運用でカバーする必要があります。
一方、KMSの非対称鍵(用途 SIGN_VERIFY)を使ったSign操作では、秘密鍵部分はAWS KMSのHSM(Hardware Security Module、鍵を安全に生成・保管する専用ハードウェア)境界を一度も離れません。AWS社員を含め誰も平文の鍵materialにアクセスできない設計で、KMS非対称CMK(Customer Master Key、KMSにおける鍵の管理単位)の秘密鍵は仕組み上エクスポートするAPIそのものが存在しません。
なお、JWTでよく使うRS256やES256に対応する署名アルゴリズムはKMSがサポートしているので、「KMSに寄せるとJWTの署名方式が制限される」という心配は基本的に不要です。
「鍵の実体がアプリに渡ってくるのか、最初から最後までKMSの中に留まるのか」——これがこの軸での本質的な違いです。
4. 判断軸2: レイテンシ・可用性・既存ライブラリとの相性
Secrets Managerパターンは、一度取得してキャッシュしてしまえば、以降の署名はプロセス内で完結します。ネットワーク往復はありません。
対してKMSはクラウドネットワークサービスであり、Sign操作は毎回API呼び出しが発生します。トークンを発行するたびにネットワーク越しの呼び出しが挟まる形です。KMSにはAPI・鍵タイプ別のリクエストクォータもあり、たとえばRSA鍵を使った暗号操作は、アカウント・リージョンごとに秒間1,000リクエストというクォータになっています。これはSign/Verify/Encrypt/Decrypt等をまとめて共有する枠なので、他の操作を呼ばずSignだけに使えば理屈上は最大1,000リクエスト/秒まで出せますが、他の操作と混在させるほど実質的な上限は下がります。超過するとスロットリングされ、バックオフ&リトライが必要になります。ログイン時など短時間にトークン発行が集中するワークロードでは、この上限が実質的な制約になり得ます。
もう一つ見落とされがちなのが、主要なJWTライブラリの多くがKMS越しの署名をネイティブ対応していないことです。jsonwebtoken や jose といったライブラリが定番であり、そのライブラリは生の秘密鍵materialを渡してローカルで署名する設計が前提になっています。KMSで署名する場合は、次のいずれかの対応が必要になります。
- 署名関数を外部から差し替えられるフォーク/派生ライブラリ(例:
jsonwebtoken-signer)を使う - ライブラリを使わず、AWS SDK(Software Development Kit、AWSをプログラムから操作するための開発キット)の
SignAPIを直接呼び、JWTのheader.payload結合・署名結果のbase64url化を自前で実装する(2章のKMSパターンのコード例がこの形です) - KMSでのJWT署名に特化したサードパーティのラッパーライブラリ(
jwt-kms、kms-jwt、aws-kms-jwtなど)を使う
どれを選んでも「定番ライブラリにそのまま鍵を渡す」ほどの手軽さはなく、実装・保守コストとして見積もっておく必要があります。
5. 判断軸3: ローテーションのしやすさ(kid切り替え含む)
Secrets Managerには組み込みのローテーション機構があります。createSecret → setSecret → testSecret → finishSecret という4ステップのLambda関数を用意し、ローテーション中は AWSCURRENT(現在のバージョン)/AWSPENDING(保留中のバージョン)/AWSPREVIOUS(以前のバージョン) というステージングラベルでバージョン管理されます。ただし、RDSなど一部サービス向けのテンプレートは用意されているものの、JWT署名鍵のようなカスタムシークレットは自分でLambdaを実装する必要があります。
KMSは、自動キーローテーションが対称鍵(暗号化用の鍵)のみに対応しています。非対称鍵やHMAC(Hash-based Message Authentication Code、ハッシュ関数を使ったメッセージ認証符号)鍵、インポートした鍵materialは自動ローテーションの対象外と公式ドキュメントに明記されています。つまり、非対称鍵のローテーションは新しいCMKを作成し、参照を切り替える手動運用が前提です。
これは一見デメリットに見えますが、JWTの kid(Key ID)ヘッダで複数の鍵を並行運用する設計とは相性が良い側面もあります。新しいCMKを作り、kid で新旧を区別しながら、検証側は両方の公開鍵を一定期間受け付ける——という王道のロールオーバー運用が、KMSの「手動で新しい鍵を作る必要がある」という制約と自然に噛み合います。Secrets Manager側でJWT鍵のローテーションを実装する場合も、結局は同様に新旧の鍵ペアを一定期間共存させる設計が必要になるため、「自動化されているかどうか」よりも「kidを使った並行運用をどう設計するか」の方が本質的な論点とも言えます。
6. 判断軸4: コスト
料金の構造がそもそも異なります。
Secrets Manager
- シークレット保管: $0.40 / シークレット / 月
- APIコール: $0.05 / 10,000リクエスト
KMS
- CMK保管: $1 / CMK / 月(対称・非対称とも同額)
- APIコール: 対称鍵操作は $0.03 / 10,000リクエスト、非対称鍵の操作(Sign, Verify等)は $0.15 / 10,000リクエスト(対称鍵操作の5倍)
KMSには月20,000リクエストの無料枠がありますが、非対称鍵の操作はこの無料枠の対象外です。つまりSign APIの呼び出しは実質すべて課金対象になります。たとえば10万回のSignリクエストで$1.50、といった計算になります。署名頻度が高いサービスほど、この差が効いてきます。
見落としやすい点として、Secrets Manager側にも隠れたKMSコストが発生することがあります。シークレットをカスタマー管理のKMSキーで暗号化している場合、GetSecretValue のたびに裏で KMS の Decrypt が1回呼ばれ、その分のAPI課金が発生します(デフォルトのAWS管理キー aws/secretsmanager を使えばこの追加課金は発生しません)。「Secrets Managerだから鍵関連のAPI課金が一切ない」とは言い切れない、という点は覚えておく価値があります。
7. 判断軸5: IAM権限設計・監査性
Secrets Managerは、IAM(Identity and Access Management、AWSの権限管理の仕組み)ポリシーやリソースポリシーで secretsmanager:GetSecretValue を制御できます。CloudTrailは取得の呼び出し自体(誰が・いつ・どのシークレットを取得したか)を記録しますが、取得した秘密鍵がその後どう使われたか(何回署名に使ったか等)は記録の対象外です。取得後の利用はアプリケーション側のブラックボックスになります。
KMSは、キーポリシー・IAMポリシー・grantsの組み合わせで、鍵単位・操作単位(kms:Sign など)に権限を絞り込めます。そしてCloudTrailは、鍵の管理操作から Sign/Verify などの暗号操作まで、すべてのAPI呼び出しを記録します。呼び出し元のIDや対象の鍵ARN(Amazon Resource Name、AWS上のリソースを一意に指す識別子)もイベントに含まれるため、「誰がいつこの鍵で署名したか」という操作そのものを監査ログから追跡できます(ただし、署名対象のメッセージ本体や署名結果自体はログに含まれません)。
「秘密鍵を取得できる人を絞る」までで十分か、「署名という操作そのものを鍵単位で監査したい」までコンプライアンス要件があるかで、この軸の重みは変わってきます。
8. まとめ — 5つの軸を並べてみる
ここまでの判断軸を一覧にまとめます。
| 判断軸 | Secrets Manager | KMS |
|---|---|---|
| 鍵materialの露出 | 平文で取得、以降はアプリ管理 | HSM境界の外に出ない、エクスポート不可 |
| レイテンシ・ライブラリ相性 | ローカル署名、既存ライブラリそのまま使える | 毎回API呼び出し、クォータあり、主要ライブラリは非対応でカスタム実装が必要 |
| ローテーション | カスタムシークレットは自前Lambda実装が必要 | 非対称鍵は自動ローテーション非対応、kid切り替えを前提とした手動運用 |
| コスト | $0.40/シークレット/月 + $0.05/1万リクエスト(裏でKMS課金が乗ることも) | $1/CMK/月 + 非対称操作は$0.15/1万リクエスト(無料枠対象外) |
| IAM・監査性 | 取得の可否は制御・監査できるが、取得後の利用は追跡不可 | 署名操作そのものを鍵単位でIAM制御・CloudTrail監査できる |
こうして並べると、「鍵の実体を外に出したくない」「署名操作そのものを監査したい」という要件が強いならKMS、「実装をシンプルに保ちたい」「署名頻度が高くAPI課金を抑えたい」「既存のJWTライブラリ資産をそのまま使いたい」ならSecrets Manager、という傾向は見えてきます。ただし、これはあくまで一般的な傾向です。実際の判断では、自分のチームがどの軸を重視するか——コンプライアンス要件、運用体制、署名頻度、実装に割けるコスト——を洗い出した上で当てはめるのが確実だと思います。
参考
- Sign - AWS KMS API Reference
- Digital signing with the new asymmetric keys feature of AWS KMS(AWS公式ブログ)
- Key spec reference - AWS KMS
- Secret encryption and decryption in AWS Secrets Manager
- GetSecretValue - AWS Secrets Manager API Reference
- Request quotas - AWS KMS
- Throttling AWS KMS requests
- Rotate AWS KMS keys
- Enable automatic key rotation - AWS KMS
- Rotation by Lambda function - AWS Secrets Manager
- Pricing - AWS Secrets Manager
- Pricing - AWS KMS
- AWS CloudTrail entries for Secrets Manager
- Logging AWS KMS API calls with AWS CloudTrail
- Key policies in AWS KMS