【AWS Tips】SSE-KMSとキーローテーションの目的・仕組み
Amazon S3の暗号化方式の1つにSSE-KMSがあります。
これはKMSというキー管理サービスを使った暗号化方式なのですが、 AWS認定セキュリティ-専門知識(SCS)の資格勉強をしているときに「キーローテーションってどういう仕組み?何のためにやるの?」という疑問がわいてきたので、少し手を動かしながら調べてみたことをまとめます。
KMSってなに?
AWS KMS(Key Management Service)は、暗号化鍵の作成・管理・運用のためのサービスです。S3の他にEBS、RDS、Redshift、Snowballなどと統合されており、保存データの暗号化に利用します。
一口に暗号化鍵と言っても、KMSが扱う鍵はマスターキーとデータキーの2種類あります。
・マスターキー(CMK):データキー自体を暗号化/復号するための鍵
・データキー(CDK):暗号化したいファイルやデータを暗号化/復号するための鍵
ちなみに、KMSで保管しているのはCMKだけらしいです。CMK・CDKを同じ場所に保管して一緒に盗まれたらリスクですもんね。
ユーザはKMSでマスターキーを作った後、データキーも作るのですが、「マスターキーで暗号化された状態のデータキー」と「平文状態のデータキー」の両方を取得します。
オブジェクトは平文データキーを使って暗号化し、暗号化されたデータキーと一緒にS3などに格納します。
このとき、ユーザの手元に残っている平文状態のデータキーを削除することが必要です!平文データキーが盗まれると、KMSで厳重管理しているマスターキーが意味をなさなくなってしまいます・・・。
この辺は次の記事が分かりやすいです。2014年のKMSが発表された当時の記事ですが、おかげで頭の中を整理することができました。
SSE-KMSってなに?
S3の暗号化方式には大きく次の5種類あります。
暗号化する場所 | 名前 | 概要 |
サーバサイド (Server Side Encryption; SSE) |
SSE-S3 | S3が管理する暗号化鍵でオブジェクト保存時に自動的に暗号化/復号してくれる。 |
SSE-KMS | KMSで管理する暗号化鍵でオブジェクト保存時に自動的に暗号化/復号してくれる。 | |
SSE-C | ユーザ自身が用意した暗号化鍵でオブジェクト保存時に自動的に暗号化/復号してくれる。 | |
クライアントサイド (Client Side Encryption; CSE) |
CSE-KMS | S3格納前に、ユーザの手元でKMSの暗号化鍵を使ってオブジェクトを暗号化しておく。 |
CSE-C | S3格納前に、ユーザの手元でユーザ自身が用意した暗号化鍵を使ってオブジェクトを暗号化しておく。 |
上でマスターキー/データキーを解説した絵は、CSE-KMSをイメージしたものです。
特に社内規定などがガチガチでなければ、SSE-S3が一番らくちんです。暗号化/復号に追加料金もかかりません。
ただ、SSE-S3だとオブジェクトの取得権限さえあれば、だれでも復号・ダウンロードできてしまいます。一方でSSE-KMSの場合は、オブジェクト権限の他に、KMSのアクセス権限もないと復号・ダウンロードができません。
なので、例えば誤ったバケットポリシーを設定し、バケットを公開してしまったとしても、SSE-KMSなら想定外ユーザからのアクセスを防御することができます。
今回はここから「SSE-KMS」に絞って話を進めます。
キーローテーションってなに?
さて、ここからが私が資格勉強中に疑問に思った箇所に入ってきます。
KMSで生成するデータキー(CMK)を運用するにあたってのベストプラクティスである、キーローテーションです。
キーローテーションとは
KMSが管理してくれているマスターキー(CMK)ですが、定期的に新しい鍵へローテーションするのが推奨の運用です。
KMSで作ったCMKは、下の画面で毎年自動的にローテーションするかを設定できます。
カスタマー管理型キーの自動ローテーションを有効にすると、AWS KMSは毎年KMSキーの自動暗号化マテリアルを生成します。AWS KMSは、KMSキーの古い暗号化マテリアルを永続的に保存するため、KMSキーが暗号化したデータを復号化するために使用できます。AWS KMSは、KMSキーを削除するまで、ローテーションされたキーマテリアルを削除しません。
ローテーションをしても、KMSは古いマスターキーは保持していて、復号での利用は継続できるようです。
ちなみに、別途作成したマスターキーをインポートした場合や、もっと高頻度にローテーションしたい場合は、手動ローテーションを行うことになります。
キーローテーションの目的
何となくイメージは湧きます。CMKに万が一不正アクセスされた場合に備えて、定期的に鍵ごと変えておくってことですよね。。。。
でも待ってください。古いマスターキーは復号には有効なんですよね?
「不正者によるマスターキー操作で防ぎたいのは暗号化ではなく復号のはず。マスターキーが流出して復号できちゃったら、ローテーションしても意味ないじゃん!!」
という疑問が出てきたわけです。
いろいろ調べて分かったことを先に書きますと、
ということのようです。
どういうことかはローテーションの仕組みを理解する必要があるので、そこを深堀ってみます。
キーローテーションの仕組み
自動ローテーションと手動ローテーションでやや動きが違います。
自動ローテーションの場合
1年に1度の自動ローテーションは、下記の特徴があります。
・エイリアス、Key ID、Key ARNは新キーに引き継がれます
・Key IDまたはKey ARN、エイリアスを参照するアプリケーションを変更する必要がありません。
(出典)Rotating AWS KMS keys - AWS Key Management Service
上の絵でいくとKey ID(とKey IDにリージョン+AWS IDを付与したKey ARN)は、新旧マスターキー群を特定する情報のイメージです。
Key IDと物理的なマスターキー(上の絵のKey material)は1対Nの関係で、ローテーションをするごとにN→N+1と増えていきます。
エイリアスは人間が分かりやすいようにKey IDに付与した「別名」です。CLIを使えば、Key IDに付与したエイリアスを更新・削除することができます。
アプリケーションがマスターキーを特定するときは、エイリアス・Key ID・Key ARNのいずれかを指定しますが、自動ローテーションでこれらの値が引き継がれるため、アプリケーションは特に改修不要、ということです。
なお、KMSコンソールだと下記のようにそれぞれの値を確認できます。
で重要なのは、Key Material=111122223333で暗号化したデータキーは、同じKey Materialでないと復号できないということです。
下図のように、もしKey Material=111122223333が流出してしまったとしても、それで被害を受けるのは、そのマスターキーで暗号化されたデータキー1だけです。
ローテーション後のKey Material=999988887777で暗号化したデータキー2は復号することはできません。
これにより「想定外に復号されてしまうデータキー・ファイルを一部に限定できる」を実現しています。
手動ローテーションの場合
より高頻度にローテーションしたい場合は、手動ローテーションが必要ですが、その場合は自動ローテーションとは異なる次の特徴を持ちます。
・Key ID、Key ARNは新キーに引き継がれません。
・エイリアスはユーザが新キーに紐づけ直せば、引き継げます。
・Key IDまたはKey ARNを参照するアプリケーションは、参照先を変更する必要があります。
(出典)Rotating AWS KMS keys - AWS Key Management Service
手動の場合「ローテーション」とは言いつつ、ほとんど「新しいマスターキーを作り直す」に等しいです。
エイリアスだけは、AWS CLIのaws kms update-aliasコマンドを使って別のKey IDのマスターキーにエイリアスを引き継ぐことができます。
ですが、ここでも疑問点が出てきます。
「結局Key Material=111122223333で暗号化したデータキーは、同じKey Materialでないと復号できないんだから、昔発行した暗号化済のデータキーは昔のKey IDを指定して復号しないといけないの?Key IDを覚えておく必要があるの?」
実は暗号化済データキーには「どのKey IDのマスタキーで暗号化されたのか」というメタデータも内部的に含んでいます。
aws kms decryptコマンドでデータキーを復号するのですが、このコマンドで指定必須のオプションは、暗号化済データキー(=を示す文字列)だけです。
つまりユーザがマスターキーのKey ID(またはKey ARN)を意識するのは原則データキーの発行・暗号化の時だけで、アプリケーションがエイリアスを基に動くように実装しておけば、常に新しいKey IDのマスターキーを使うことができます。
実際に実機で確認してみる
机上で概念・仕組みの整理は出来たので、実際に実機で確認してみようと思います。
バケットの作成
バケットのデフォルト暗号化にSSE-KMSを設定すれば、以後ユーザは意識にしなくてもアップロードすれば自動的にファイルが暗号化されます。
デフォルトの暗号化は、バケット作成時に下記の様な設定を行えばOKです。
KMSキーは事前に作成しておいた「カスタマー管理型のキー」を使います。
ファイルの暗号化(マネジメントコンソールから)
格納したいバケットから下記の通り、ファイルをアップロードしてみましょう。
ここでは「sse-kms-test-20210909」バケットに、「test20210909.txt」というファイルをアップロードしました。
アップロード後にファイルのプロパティを見てみると、下のように自動的に指定したKMSキーで暗号化されました。
ファイルの暗号化(CLIから)
バケットにデフォルト暗号化を設定している場合は、マネジメントコンソールの場合と同様、特に意識することなく自動で暗号化されます。
Cloud9から下記コマンドでファイルをアップロードしてみました。
マネジメントコンソールから見るとバケット内に対象のファイルが格納されてますね。
ファイルのプロパティを見ると、マネジメントコンソールの時と同じようにデフォルト暗号化に指定したキーで暗号化されていました。
ちなみに、バケットに設定したデフォルト暗号化キーとは違うキーを指定して、明示的に暗号化させることもできます。
先ほどまで利用していたキーID末尾が[199]のキーに加えて、末尾[61c]のキーを作ってみました。
CLIで--sseオプションでaws:kmsを、--sse-kms-key-idオプションで末尾[61c]の暗号化キーのARNを指定し、ファイルをS3にアップロードしてみました。
S3に格納されたファイルをプロパティを見ると、末尾[61c]のキーで暗号化されています!
※ちなみに、デフォルト暗号化キーとは別のキーでの暗号化は、アップロード時に「プロパティ>サーバー側の暗号化設定>暗号化キーを指定する>デフォルトの暗号化バケット設定を上書きする」を選ぶことで、マネジメントコンソール越しでもできます。
ファイルの復号
バケットポリシーでオブジェクトの取得(GET)権限と、KMSキーへのアクセス権限があれば、暗号化と同様、ユーザは特に意識することなく復号・ダウンロードできます。
では、KMSキーのアクセス権限が無い場合はどうなるでしょうか?
期待する挙動は、復号・ダウンロードしようとするとアクセス不足でエラー、です。
それでは「AmazonS3FullAccess」ポリシーを持っているが、KMSに関するポリシーは持っていないユーザでダウンロードを試みます。
S3コンソールでファイルを選択するところまでは正常に動作しますが、ここで「ダウンロード」ボタンを押してみると・・・
想定通り、Access Deniedになりました!
キーローテーションしてみる
バケットのデフォルト暗号化キーに設定している末尾[199]のキーが古くなったという想定で、新しいキーにローテーションしてみます。
手動キーローテーションは次のステップでCLIを実行します。
- 今のエイリアスとキーの紐づけを確認する(list-aliases)
- 新しいマスターキーを作成する(create-key)
- エイリアスを新しいキーに付け替える(update-key)
- 再度エイリアスとキーの紐づけを確認する(list-aliases)
- キーポリシーの修正
1. 今のエイリアスとキーの紐づけを確認する(list-aliases)
次のコマンドで存在するエイリアスと紐づくKey IDを確認します。
レスポンスの一番右が紐づくKey IDです。sse-test-keyというエイリアスに末尾[199]が紐づいていますね。
2. 新しいマスターキーを作成する(create-key)
次に新しいキーを作成します。末尾[976]のKey IDを持つマスターキーが作成されました。
3. エイリアスを新しいキーに付け替える(update-key)
次のコマンドでエイリアスを付け替えます。このコマンドはレスポンスに何も帰ってこないため、次で結果を確認します。
4. 再度エイリアスとキーの紐づけを確認する(list-aliases)
1.と同じコマンドで再度紐づけを確認すると、sse-test-keyが末尾[976]のKey IDを持つキーに紐づきました。
ちなみにマネジメントコンソールで見てみると、末尾[199]の古いキーには何もエイリアスが紐づいていませんね。ただしステータスは有効なままです(無効にしてしまうと復号できなくなります)。
5. キーポリシーの修正
新しい末尾[976]のキーには、まだキーポリシーを設定していません。
ローテーション前のキーと同じポリシーを設定してください。
これはマネジメントコンソールで旧キーの「キーポリシー」を開き、ポリシービューの文字列(下図の赤枠)をコピーして、新キーにペーストする方法が簡単かと思います。
S3のデフォルト暗号化のキーを変更する
上記でエイリアスを引き継いで新キーにローテーションしたわけですが、SSE-KMSはエイリアスではなく、キーARNで暗号化キーを指定しています。
これだと旧キーを使い続けてオブジェクトの暗号化をしてしまうので、手動で変更します。
パケットのプロパティを開きます。「デフォルトの暗号化」を見ると、キーARNがまだ末尾[199]になっているので、右上の「編集」ボタンをクリックします。
キーARNを指定できるので、新しく作成した末尾[976]のキーARNを指定して「変更の保存」ボタンをクリックします。
これで今後格納されるオブジェクトは新しいキーで暗号化されるようになりました。
最後に、これまで旧キーで暗号化されていたオブジェクトはどうなるのでしょうか?
・
・
・
答えは「旧キーで暗号化されたまま。ダウンロード時に旧キーで自動復号される」です。
もし新キーで再暗号化されてしまうと、せっかくローテーションしたのに新キーが漏洩するとバケット内の全ファイルが不正アクセスの危機にさらされてしまうので、ローテーションの意味がないですよね。なので、この動きが正しいと思います。
実際に、マネジメントコンソールからアップした「test20210909.txt」のプロパティを見てみると、最初と変わらず末尾[199]のキーで暗号化されたままでした。
旧キーのステータスが有効なままであれば、引き続きそのキーへのアクセス許可があるユーザはダウンロードと同時に復号されます。
まとめ
SSE-KMSの実装方法と、キーローテーションの目的・仕組みを整理しました。
個人的に疑問だったキーローテーションの目的と、ローテーション後の復号の仕組みが理解できてすっきりしました。
KMS周りは複雑で、システムの機能要件にダイレクトに効いてくるサービスではないので学習がないがしろになりがちですが、これで少し苦手意識がなくなりそうです。
※参考サイト
10分でわかる!Key Management Serviceの仕組み #cmdevio | DevelopersIO
[AWS Black Belt Online Seminar] AWS Key Management Service
AWS CLI を使用した既存の Amazon S3 オブジェクトの暗号化 | Amazon Web Services ブログ
Rotating AWS KMS keys - AWS Key Management Service