KMSで認証情報を暗号化しLambda実行時に復号化する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データベースやAPIサーバーなど外部システムと連携する際には、往々にして認証情報が必要になります。

今回は AWS Key Management Service (以下 KMS) の共通鍵暗号の仕組みを使い、暗号化した認証情報を AWS Lambda 関数のコードに埋め込み、関数呼び出し時に認証情報を復号化する方法を紹介します。

decrypt-sensitive-data-with-kms-on-lambda-invocation

基本的なアイデアは次のブログで書かれており、KMS を使った暗号化処理だけを自分向けメモも兼ねて抜き出しました。

http://ijin.github.io/blog/2015/08/06/github-to-lambda-to-slack/

KMS ではマスターキーを使って暗号・復号する処理が API で切り出されているため、この API を使って認証情報を暗号化します。

KMS と Lambda の連携

以下の流れで動作確認します。

  1. AWS KMSマスターキーの作成
  2. AWS CLI からマスターキーを使って暗号・復号処理を確認
  3. AWS Lambda から暗号化されたデータ(ciphertext)を復号して出力
  4. AWS Lambda 関数に KMS:Decrypt 権限を与える
  5. AWS Lambda 関数を実行して復号されていることを確認

KMS マスターキーの作成

まずは Lambda 関数用のマスターキーを作成します。

マネージメントコンソールにログインし、「IAM→Encryption Keys」と選択すると、 KMS の管理画面に移動できます。 KMS 単独のメニューは存在しないので、ご注意ください。

kms_management_console

KMS の鍵はリージョンごとに個別に存在します。 Filter のプルダウンで鍵を作成するリージョンを選択し、「Create Key」をクリックして、マスターキーの作成画面に移動します。

Key Administrative PermissionsKey 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

# 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):
    kms = boto3.client('kms')
    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 のユースケースの紹介でした。