この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
データベースやAPIサーバーなど外部システムと連携する際には、往々にして認証情報が必要になります。
今回は AWS Key Management Service (以下 KMS) の共通鍵暗号の仕組みを使い、暗号化した認証情報を AWS Lambda 関数のコードに埋め込み、関数呼び出し時に認証情報を復号化する方法を紹介します。
基本的なアイデアは次のブログで書かれており、KMS を使った暗号化処理だけを自分向けメモも兼ねて抜き出しました。
http://ijin.github.io/blog/2015/08/06/github-to-lambda-to-slack/
KMS ではマスターキーを使って暗号・復号する処理が API で切り出されているため、この API を使って認証情報を暗号化します。
KMS と Lambda の連携
以下の流れで動作確認します。
- AWS KMSマスターキーの作成
- AWS CLI からマスターキーを使って暗号・復号処理を確認
- AWS Lambda から暗号化されたデータ(ciphertext)を復号して出力
- AWS Lambda 関数に KMS:Decrypt 権限を与える
- AWS Lambda 関数を実行して復号されていることを確認
KMS マスターキーの作成
まずは Lambda 関数用のマスターキーを作成します。
マネージメントコンソールにログインし、「IAM→Encryption Keys」と選択すると、 KMS の管理画面に移動できます。 KMS 単独のメニューは存在しないので、ご注意ください。
KMS の鍵はリージョンごとに個別に存在します。 Filter のプルダウンで鍵を作成するリージョンを選択し、「Create Key」をクリックして、マスターキーの作成画面に移動します。
Key Administrative Permissions と Key Permissions には普段利用している管理系ユーザーを選択します。
AWS CLI から暗号・復号処理を確認
プログラムを書く前に、CLIから暗号・復号操作を確認します。
マスターキーの一覧を確認します。
$ aws kms list-keys
{
"Keys": [
{
"KeyArn": "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz",
"KeyId": "xxx-yyy-zzz"
}
],
"Truncated": false
}
マスターキーのARNを環境変数に設定します。 KeyIdとエイリアスの2通りの指定があります。
$ export KEYID=arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz # KeyId
$ export KEYID=arn:aws:kms:ap-northeast-1:123456789012:alias/lambda # Alias
マスターキーを使って暗号化
マスターキーで暗号化するには Encrypt
API を使います。
$ aws kms encrypt --key-id $KEYID --plaintext 'hello, world!'
{
"KeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz",
"CiphertextBlob": "CiAUkK3nep3+LDfCjRPA5NDnd5NEXv5BWWjweqEySvaTLBKUAQEBAgB4FJCt53qd/iw3wo0TwOTQ53eTRF7+QVlo8HqhMkr2kywAAABrMGkGCSqGSIb3DQEHBqBcMFoCAQAwVQYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAxHrDshpbxSGRgMAXECARCAKHfUd1sJjxRX/7tq7twil6vaXjtPZsnr9AURI1gjR+RPL4WlQTvNDjE="
}
レスポンスの CiphertextBlob
は暗号化した plaintext(つまり ciphertext) を base64 エンコードしたものです。
base64 デコードして encrypted というファイル名で ciphertext を保存します。
$ aws kms encrypt --key-id $KEYID --plaintext 'hello, world!' --query CiphertextBlob --output text | base64 --decode > encrypted
マスターキーを使って復号化
マスターキーで復号化するには Decrypt
API を使います。
$ aws kms decrypt --ciphertext-blob fileb://encrypted
{
"Plaintext": "aGVsbG8sIHdvcmxkIQ==",
"KeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz"
}
レスポンスの Plaintext
は復号化したデータ(つまり plaintext)を base64 エンコードしたものです。
base64 デコードすれば plaintext となります。
$ aws kms decrypt --ciphertext-blob fileb://encrypted --query Plaintext --output text | base64 --decode
hello, world!
Lambda 関数化
KMS で暗号化かつ base64 エンコードした文字列を Lambda 関数に予め埋め込み、Lambda 関数実行時に復号化してみます。
通常であれば、復号化したユーザーデータを元に何か処理を行うでしょうが、今回は復号化した文字列そのままレスポンスとして返します。
Lambda 関数は Python で実装します。
import base64
import boto3
kms = boto3.client('kms')
# base64 encoded ciphertext
ciphertext_blob_encoded = 'CiAUkK3nep3+LDfCjRPA5NDnd5NEXv5BWWjweqEySvaTLBKUAQEBAgB4FJCt53qd/iw3wo0TwOTQ53eTRF7+QVlo8HqhMkr2kywAAABrMGkGCSqGSIb3DQEHBqBcMFoCAQAwVQYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAxHrDshpbxSGRgMAXECARCAKHfUd1sJjxRX/7tq7twil6vaXjtPZsnr9AURI1gjR+RPL4WlQTvNDjE='
# ciphertext
ciphertext_blob = base64.b64decode(ciphertext_blob_encoded)
def lambda_handler(event, context):
dec = kms.decrypt(CiphertextBlob = ciphertext_blob)
return dec['Plaintext'] #plaintext
ciphertext_blob_encoded には ciphertext を base64 エンコードした文字列を設定します。先の例で CLI から encrypt した時の文字列を使いまわします。
KMS の操作権限を Lambda 関数に与える
Lambda 関数に設定した Role に マスターキーで復号するための権限を追加します。 Role にポリシーを追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1448696327000",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
"arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz"
]
}
]
}
Resource には KMS マスターキーの arn を設定します。
Lambda 関数を実行する
$ aws lambda invoke \
--function-name kms_demo \
--payload '' \
response.txt
{
"StatusCode": 200
}
$ cat response.txt
"hello, world!"
無事、復号されていますね。
まとめ
センシティブなデータは KMS で暗号化すれば、プログラムのソースコード内に気兼ねなくベタ書きできます。 ソースコードの取得と KMS の Decrypt API を実行できないかぎり、元データは復元できません。
KMS は使いみちがよくわからないという声をたまに聞きますが、地味だけと非常によくできる子です。そんな KMS のユースケースの紹介でした。