踏み抜く前に知る、カスタマーマネージドキーの制約と罠
안녕하신게라!パナソニック コネクト株式会社クラウドソリューション部の加賀です。
AWS環境のセキュリティ設計において、暗号化は必須の要件となることが多いです。その中心となるのが AWS KMS(Key Management Service) です。
最初は「AWSマネージドキーでポチッと暗号化すればOKでしょ?」くらいに思っていても、いざ実務で自前管理の カスタマーマネージドキー(Customer managed key。旧称 Customer Master Key / CMK) を利用しようとすると、「キーポリシーって何?」「運用どうするの?」「S3と連携したら課金が爆発したんだけど!?」などなど、思わぬ深みにハマりがちです。
実は私自身、過去にKMSに対して解像度が低い状態でシステム設計に臨み、かなり沼にハマってしまった経験があります。「あの設計を始める前に、こういうまとまった情報が読めていたら良かったかも」という当時の自分に向けた切実な思いが、この記事の執筆動機です。
本記事では、カスタマーマネージドキー自体の仕様から、実務でハマりがちな罠までを整理し、安全な運用設計の勘所をお伝えします。
キーの種類
KMSには大きく分けて3種類のキーが存在します。
| キーの種類 | 説明 | 管理主体 | コスト | ポリシー制御 |
|---|---|---|---|---|
| AWS 所有のキー | DynamoDB等のデフォルト暗号化などで使われる。ユーザからは見えない。 | AWS | 無料 | 不可 |
| AWS マネージドキー | サービスごとに自動作成されるキー(例 alias/aws/s3, alias/aws/lambda) |
AWS |
API利用料のみ有料 (キー自体の維持費は無料) |
キーポリシーの変更不可(IAMでの制御は可能だが限定的) |
| カスタマーマネージドキー | ユーザが明示的に作成・管理するキー(旧称CMK) | ユーザ |
有料 (キー維持費 $1/月 + API利用料) |
フルコントロール可能(キーポリシーで制御) |
💡 AWS Secrets Manager や Parameter Store との役割分担
「KMSでパスワードや機密データを直接暗号化して保存する」といった誤解をされることがありますが、KMSはあくまで「暗号化するための鍵」を提供するサービスです。パスワードやAPIキーなどの機密データの保存・管理自体は Secrets Manager や Parameter Store の役割であり、KMSはその裏側でデータを暗号化・保護するために併用される関係になります。
カスタマーマネージドキーを採用するシチュエーション
AWSマネージドキー(維持費無料)ではなく、あえてカスタマーマネージドキーを採用する主な理由は以下の4点に集約されます。
厳格なアクセス制御と職務分掌
KMSキーに対する「管理者(作成・変更・削除)」と「利用者(暗号化・復号のみ)」を明確に分離できます。キーポリシーを用いることで、IAM権限とは独立した非常に強力なアクセス制御が可能になります。
クロスアカウントでの利用
別のアカウントのIAMロールに対して、暗号化・復号の権限を与えることができます。
AWSマネージドキーではクロスアカウントでの共有・利用はできません。
将来的に、ProductionアカウントのS3バケットにある暗号化データをStagingアカウントのシステムから読み取る可能性が1%でも存在するなら、初期構築時からカスタマーマネージドキーを選択すべきです。後からAWSマネージドキーで暗号化された大量のデータを再暗号化し、システムを再稼働させるのは多大な労力を伴います。
(※KMSの機能としてキーポリシーでクロスアカウント共有が完結するため、そもそもAWS Resource Access Manager(RAM)を経由する仕様にはなっておらず、一貫してキーポリシー内に直接アカウントを記述して権限を委譲する方式となります。)
高度な運用・監査要件への対応
コンプライアンス要件(PCI DSS等)を満たすための詳細なコントロールが可能です。
-
暗号化コンテキストを用いた高度なアクセス制御と監査
暗号化コンテキストに任意の業務上の属性データを含めて利用することで、CloudTrailログ内にJSON形式のまま平文で記録されます。ログ記録自体はAWSマネージドキーでも行われますが、カスタマーマネージドキーの真の強みは、**「指定した暗号化コンテキスト(例:project=A)が含まれていないリクエストを、キーポリシーの Condition(kms:EncryptionContext:xxx)で強制的に拒否(Deny)できること」**にあります。これにより、インシデント調査での追跡性向上だけでなく、データの改ざん防止や厳格なアクセス制御が可能になります。
⚠️ 注意 トレードオフとしてログに平文で記録されるため、暗号化コンテキストにパスワードや個人情報(PII)などの機密情報を含めてはいけません。セキュリティ要件を満たすためのログ出力から機密データが漏洩するのは、絶対に避けなければならないアンチパターンです。 -
BYOK(Bring Your Own Key)への対応
金融機関など、コンプライアンス要件により「AWSが生成した乱数ではなく、自社のオンプレミス環境やHSMで生成した鍵(キーマテリアル)を持ち込んで使わなければならない」という要件が存在する場合、カスタマーマネージドキーによるキーマテリアルのインポート機能が必須の選択肢となります。 -
キーの自動ローテーション
有効化すると、KMSが自動的に新しいキーマテリアル(裏側の乱数)を生成します。間隔は90日〜2560日の範囲でカスタマイズ可能で、過去のデータも自動的に古いキーマテリアルを使って復号されるため、アプリケーション側での対応は不要です。
なお、AWSマネージドキーの自動ローテーション期間は「365日(昔は1095日)」固定で変更できません。そのため、PCI DSSなどで「1年未満(90日など)の使用期間ごとに鍵をローテーションすること」が求められる場合は、必然的にカスタマーマネージドキーを採用することになります。
⚠️ 注意 自動ローテーションは、AWS KMSによってキーマテリアルが作成された対称暗号化KMSキー(Symmetric key)でのみサポートされています。非対称キー(Asymmetric key、RSA等)や、ユーザが独自の鍵を持ち込んだもの(インポートされたキーマテリアル)では利用できない点にご注意ください。非対称キー等の場合は、手動で新しいKMSキーを作成し、アプリケーション側で参照するエイリアスを切り替える「手動ローテーション」の運用を自前で設計する必要があります。 -
マルチリージョンキーの作成
東京リージョンで作ったキーのレプリカを大阪リージョンなどに作成できます。
⚠️ 注意 実はKMSのキーポリシーやエイリアス自体はリージョンごとに独立しており、同期されません。さらに、レプリカ先のリージョンでも「1つの独立したKMSキー」として月額1ドルの維持費がそれぞれ発生するため、運用管理の手間とコストが倍増します。「DR要件だからとりあえず……」と初期段階から安易に採用するのは避け、必要な場合のみ採用すべきです。
暗号的消去(Crypto Shredding)
コンプライアンス等でデータの確実な破棄が求められる際、ストレージの物理破壊をしなくても「対象のカスタマーマネージドキーを削除するだけ」で、その鍵で暗号化されていた全データが存在はしていても論理的に二度と解読不可能になります。一瞬でデータ破棄を証明できる強力なメリットです。
カスタマーマネージドキーの制約と運用上の注意点
即時削除はできない(削除待機期間)
KMSキーを誤って削除してしまうことは、前述の「暗号的消去」が意図せず発動してしまうことを意味します。強力な機能である反面、誤操作時は対象のデータすべてが復号不可能になるという致命的な影響を及ぼすため、KMSでは単一の操作による即時削除はシステム上できないよう保護されています。
まず削除のスケジュール設定を実施し、指定した待機期間(7日〜30日)が経過して完全に削除されると、二度と復旧不可能になります。待機期間中であれば削除のキャンセル機能で復旧可能です。
運用上のベストプラクティス(DisableKeyの活用)
不要になったキーをいきなり削除待機状態(PendingDeletion)にするのではなく、まずは「キーの無効化(DisableKey)」を行い、システムや業務に影響が出ないかを数日〜数週間監視してから削除スケジュールに入れるのが鉄則です。
削除スケジュール(ScheduleKeyDeletion)のアラート監視
「誤って削除してしまう」対策として DisableKey の運用は重要ですが、万が一管理者権限を持つ何者かが直接 ScheduleKeyDeletion を実行してしまった場合に備え、CloudTrail + EventBridge + SNS などで ScheduleKeyDeletion APIの発行を検知して即時発報する仕組み を作っておくのが本番運用の定石です。
IaC運用の罠
Terraform等で誤って terraform destroy が走った場合、古いキーが PendingDeletion 状態で残り、同名のエイリアス作成時に競合エラーになる等、CI/CDが停止するトラブルが起きがちです。
非対称キーのスロットリング制約
対称キー(Symmetric key)に対して、非対称キー(Asymmetric key、RSA等)を利用した場合、APIのTPS(1秒あたりのリクエスト上限)が極端に低く設定されています(対称キーがリージョンにより 5,500〜50,000 リクエスト/秒であるのに対し、非対称キーは 500 リクエスト/秒など)。オートスケールするシステムなどで利用すると容易にスロットリングの上限に達する可能性がある点に注意が必要です。必要に応じてService Quotasから上限緩和申請を行うことも検討してください(ただし非対称キーの緩和は難しい場合があります)。
VPCネットワーク設計への影響(PrivateLink)
インターネットに抜けられないプライベートサブネットからKMSへアクセスする場合、KMS用のVPCエンドポイント(Interface Endpoint) が必要になります。これを忘れると通信タイムアウトになります。
運用上のベストプラクティス / アンチパターン
【アンチパターン】高頻度アクセスへの単純適用によるAPI課金とログの急増
実務で最も頻発する悲劇が、「S3の暗号化にKMSを指定し、数百万のオブジェクトに対してGET/PUTが発生して数千ドルのAPI課金が請求される」ケースです。
KMSのAPI利用料は、カスタマーマネージドキーでもAWSマネージドキー(alias/aws/s3 など)でも全く同様に発生します。さらに、KMSのAPI呼び出しは一律で「CloudTrailの管理イベント」として記録されるため、API課金だけでなく、CloudTrailからCloudWatch LogsやDatadog等の監視基盤へのログ取り込み(インジェスト)費用が想定外に跳ね上がる二次災害も引き起こしがちです。
KMSの重要な仕組みの話になりますが、KMSの直接暗号化API(Encrypt)は、対称キー(Symmetric key)の場合は最大4KBまでというハードリミットが存在します。さらに非対称キー(Asymmetric key)を利用する場合は、鍵長とパディングアルゴリズム(OAEP等)の組み合わせによって数百バイト程度までしか暗号化できないというさらに厳しい制限がかかります。そのため、KMSはデータそのものを直接暗号化するのではなく、KMSが発行した保護用の「データキー」を用いて対象のデータを暗号化する、エンベロープ暗号化(Envelope Encryption)の仕組みを採用しています。EBSやRDSなどの場合、インスタンスの起動時やボリュームのマウント時にデータキーを一度復号し、その後はハイパーバイザ等のメモリ上に保持して継続利用するため、継続的なKMSへのAPIコールは発生しません。しかし、S3やSQS、CloudWatch Logsなど、高頻度でメッセージング・ストリーミング処理を行うサービスでは、設定次第でデータキーのやり取りが毎回発生し、同様のコスト爆発に直結しやすい点に注意が必要です。
【対策】 S3の場合は必ず「S3 バケットキー(S3 Bucket Keys)」を有効化してください。この機能を有効にすることで、一時的なデータキーがキャッシュされ、KMSへのAPIコールを最大99%削減でき、連動してCloudTrailの記録コストも激減します。SQSの場合はデータキーの再利用期間設定を行うことで回避します。
実は2023年末のアップデートにより、マネジメントコンソールから新規作成されるS3バケットでは S3 Bucket Keys がデフォルトで有効化されるようになりました。しかし、既存のバケットや、古いTerraform/CloudFormationモジュールを流用して作成されたバケットでは無効になっているケースが依然として非常に多いため、IaC等で構築する際は明示的に有効化パラメーターを記述するのが現在の鉄則です。
⚠️ 注意 コスト削減と厳格な監査要件のトレードオフ
S3 Bucket Keysはコストを激減させる反面、「バケット単位の記録のみとなり、オブジェクト単位の復号記録(いつ誰がどのファイルを復号したか)がCloudTrailに残らなくなる」というトレードオフがあります。
また、CloudTrailの記録コストを抑える手段として、「高度なイベントセレクタ」を用いて特定のKMS API(EncryptやGenerateDataKeyなど)のイベント記録を除外する構成も強力な防衛策ですが、これも同様に追跡性を低下させます。
PCI DSSや金融系など、完全なアクセス監査が求められるシステムでは、コスト増を受け入れてでも「S3 Bucket Keysの無効化」および「APIイベントの全量記録」を検討する必要があります。
(※逆に詳細な監査が不要で「保存時の暗号化」だけが目的ならば、KMSを使わず完全無料の SSE-S3 を選ぶのがベストプラクティスです。)
【ベストプラクティス】kms:ViaService等によるアクセス制限
キーポリシーを設定する際、単に「対象のIAMロール」を許可するだけではなく、「どのAWSサービス経由の呼び出しなのか」や「自分たちのどのリソースからの呼び出しか」を制限することが重要です。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow_S3_Access_Only",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/MyApplicationRole"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": "s3.ap-northeast-1.amazonaws.com"
},
"StringLike": {
"kms:EncryptionContext:aws:s3:arn": [
"arn:aws:s3:::my-secure-bucket",
"arn:aws:s3:::my-secure-bucket/*"
]
}
}
}
]
}
これで意図しない直接のAPIコールをブロックできます。
S3 Bucket Keys が無効な場合、暗号化コンテキストに渡される対象ARNは個別の「オブジェクトARN(.../bucket/*)」になりますが、S3 Bucket Keys を有効にした場合はバケットレベルでデータキーが生成されるため、渡されるARNは「バケット自体のARN(.../bucket)」になります。そのため、上記のようにリスト形式で両方のディメンションを許可する必要があります。
なお、「混乱した代理(Confused Deputy)問題」を防ぐために一般的には aws:SourceArn 等が推奨されますが、S3の通常のGet/Put操作においてはユーザのIAM権限を利用してS3がKMSを呼び出しているため、S3サービスプリンシパルからのアクセス扱いにはならず、KMSリクエストに aws:SourceArn は含まれません。そのためS3のアクセス制限においては aws:SourceArn ではなく、上記のように kms:EncryptionContext:aws:s3:arn を利用するのが正解です。
⚠️ 補足 AWSのポリシー評価の仕様として、上記のように異なる条件キー(kms:ViaService と kms:EncryptionContext 等)を並べて記述すると、すべてAND条件として評価されます(※同じキー内に複数の値をリスト [] で書いた場合のみ OR 条件になります)。実際のシステムでは「IAMロールから直接APIを叩くケース」と「S3などのAWSサービス経由で叩かれるケース」が混在するため、ANDでガチガチに固めると正常な業務アクセスまで AccessDenied エラーになるトラブルが多発します。要件に合わせて Statement を適宜分割する等、慎重なポリシー設計が必要です。
【ベストプラクティス】エイリアスを活用した移行運用
KMSキーIDは 1234abcd-12ab-34cd-56ef-1234567890ab のようなUUID形式です。これをアプリケーションに直接ハードコードするのは避け、alias/app-backend-key などのエイリアスを利用します。
KMS APIの洗練された仕様として、復号(Decrypt)時にはKMSが生成した暗号文(CiphertextBlob)のデータ構造そのものの中に使われたKey ID情報が内包されているため、アプリケーション側で復号時にエイリアスやKey IDを指定する必要すらありません。
新しいキーを作ってエイリアスの参照先を切り替えるだけで、暗号化処理側のシステムを事実上無停止で移行できます(※過去のキーは削除せずに残す必要があります)。
IaC運用のポイント
Terraform等で移行する際、既存のキー自体を削除・置換するのではなく、「新しいKMSキーリソースを作成し、aws_kms_alias リソースの target_key_id だけを新しいキーに向き先変更する」という形をとります。
また、IAMポリシー等で権限リソースを指定する際、Resource: "*" で Condition: {"ForAnyValue:StringLike": {"kms:ResourceAliases": "alias/my-key"}} のようにエイリアスでアクセス制限をかけることができます。しかし、この制御をかけた状態で前述の「エイリアス移行」を実施すると、既存データの復号が100%失敗する大事故に繋がります。
KMSの暗号文には「暗号化に使われたキー自体のID」が書き込まれています。復号リクエスト時、KMSはその「古いキー」に対して権限チェックを行いますが、新しいキーにエイリアスを付け替えた時点で古いキーからはエイリアスが外れており、kms:ResourceAliases の条件を満たせなくなり AccessDenied エラーとなります。
対策として、移行が想定される環境ではエイリアスでの制御ではなく、タグベースのアクセスコントロール(aws:ResourceTag/Environment 等)を利用してIAMポリシーを縛ることを推奨します。やむを得ずエイリアスで制御している場合、移行前に旧キーへ別の退避用エイリアス(alias/my-key-old 等)を付与し、ポリシー側で新旧両方のエイリアスを許可する方針をとってください。
【アンチパターン】KMS Grants(権限委譲)の設計漏れによる非同期処理の失敗
実務でLambdaへの権限付与忘れと並んで頻発するのが、KMS Grants(グラント) の考慮漏れです。
KMSにはキーポリシーやIAMポリシーといった静的な権限とは別に、AWSサービスが「ユーザに代わってバックグラウンドで非同期にKMSを利用する」ための Grants という仕組みが存在します。
例えば、カスタマーマネージドキーで暗号化されたAMIを使ってEC2 Auto Scalingグループを起動する場合や、RDSの自動バックアップ(スナップショット)を取得する場合などです。AWSマネージドキーを利用している場合はAWSがよしなに Grants を発行してくれますが、カスタマーマネージドキーの場合は、対象のサービス(Auto Scalingのサービスリンクロール等)に対して、利用者が明示的に kms:CreateGrant 等の権限を付与しておく必要があります。
これを忘れると、「(権限を持つユーザが実行した)インスタンスの起動リクエスト自体は成功するのに、裏側でEBSの復号に失敗してインスタンスが即座にTerminateされ無限ループする」という非常に調査が困難な障害に繋がります。
【アンチパターン】Lambdaの環境変数暗号化での権限付与忘れ
Lambdaの環境変数をカスタマーマネージドキーで暗号化する場合、Lambdaの「実行ロール」に対して kms:Decrypt 権限を与える必要があります。これは、Lambda関数が起動(初期化)する際、AWSサービス側が環境変数を復号するためにその実行ロールの権限を使用する仕様だからです(※AWSマネージドキー alias/aws/lambda の場合はロールへの明示的な権限付与は不要です)。これを忘れると、「ローカル実行や自分の強力なIAM権限でテストは動くのに、デプロイするとエラーになる」という初歩的な罠にハマります。
【アンチパターン】アカウントレベルのAPIリクエスト上限(TPS)への無配慮
KMSのAPI制限(クォータ/TPS上限)は、アカウント・リージョンごとに設けられています。
一部のシステムが暴走してKMSのAPI制限に達すると、同じAWSアカウント内の無関係なシステム(暗号化を利用している既存システムのオートスケールによるEC2の起動や、RDSのフェイルオーバー時の再起動処理、Secrets Managerの復号など)まで密かに失敗し、他システムのスロットリングを誘発して連鎖障害を引き起こすリスクがあります。
キーポリシーの重要性とロックアウトのリスク
KMSのアクセス制御の根幹はキーポリシーです。IAM側で AdministratorAccess を持っていても、KMSのキーポリシー側で許可されていないとアクセスできません(AccessDenied エラーになります)。
ルートユーザすら弾き返す「完全ロックアウト」の罠
これはKMSを運用する上で最大の罠です。
KMSは他のAWSリソースと異なり、「キーポリシーの中でアカウントへの権限委譲が記述されていない限り、IAMポリシーの評価を一切拒否する」という特有の仕様を持ちます。
そのため、キーポリシーの編集で誤って「IAMへの権限委譲を意味する arn:aws:iam::<アカウントID>:root の許可(Allow)」を削除してしまった場合、どれほど強力なAdministratorAccess権限を持つIAMユーザであったとしても即座に操作不能となります。
ただし、この「誤って許可を消しただけ」の状態であれば救済措置があります。AWSアカウントの真のルートユーザ(メールアドレスとパスワードでログインする大元のユーザ)であれば、コンソールやAPIから kms:PutKeyPolicy を実行し、自力でポリシーを修正して回復可能です。
KMSにおいてそのような例外は一部の条件下に限られます。最も恐ろしいのは、権限の「削除(暗黙のDeny)」ではなく、キーポリシー内にルートユーザや全プリンシパル("Principal": "*")に対する kms:PutKeyPolicy の「明示的な拒否(Explicit Deny)」を設定してロックアウトしてしまった場合です。こうなると最強の権限を持つルートユーザ自身も完全に締め出され、AWSサポートに連絡して対応を依頼するという、非常に面倒な手続きが必須となります。(その間、暗号化を伴う処理は操作不能な状態が続きます)
IaCでの防衛策(TerraformやCloudFormation等)
Terraform等でカスタムのキーポリシーを定義して上書きする場合、自分自身(IAM実行ロール)やアカウントへの権限委譲の許可を書き忘れると、作成直後にロックアウトする事故が多発します。
必ずTerraformの aws_caller_identity データソース(CloudFormationの場合は AWS::AccountId 疑似パラメータ等)を利用し、「自分自身の実行ロールやアカウントルート」のARNをキーポリシーの管理者権限(kms:*)に明示的に混ぜる設計とコードにしてください。
kms:PutKeyPolicy を指定し忘れるとapply時に予防的エラーとしてくれますが、それに頼らずキーポリシーを確認するようにしましょう。
まとめ
「とりあえずKMS」でカスタマーマネージドキーを選んでしまうと、権限管理の沼や思わぬAPI課金に見舞われるリスクがあります。(そのままAWSも嫌いになってしまうと目も当てられません)
導入の採用判断におけるチェックポイントとして、
- 別AWSアカウントとのデータ共有が必要か?
- キーポリシーを用いた厳格な職務・アクセス分離が必要か?
- 暗号的消去(Crypto Shredding)や、CloudTrailへのコンテキスト記録による高度な監査要件に対応する必要があるか?
これらに該当し、どうしてもカスタマーマネージドキーが必要な要件がない限りは「AWSマネージドキー」で済ませるのが無難です。
もしカスタマーマネージドキーを使う場合は、以下の2点だけは絶対に忘れないようにしてください。
- 「S3 Bucket Keys等の有効化によるAPI課金爆発の防止」
- 「IaC実行ロールを含め、自分自身の管理権限の確実な確保(ロックアウトの回避)」
これらを意識するだけで、明日の自分を救うことになります。KMSの仕様を正しく理解し、安全でお財布に優しいアーキテクチャを目指しましょう。
お断り
記事内容は個人の見解であり、所属組織の立場や戦略・意見を代表するものではありません。
あくまでエンジニアとしての経験や考えを発信していますので、ご了承ください。