3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS Lambda(python)でデータを暗号化する際のメモ

Last updated at Posted at 2021-03-11

初心者の苦闘ログとして残しておきます。

やりたかったこと

  • AWS Lambda内で画像を暗号化して、S3に保存すること
  • S3に保存されたデータは、復号鍵を持つまたは復号権限を持つ人が、復号して取得できること
  • Pythonでやること

事前準備

  • ① Lambdaを作成する
  • ② KMSのマスターキーを作成する
  • ③ KMSのデータキーを作成し、CiphertextBlobをLambdaの環境変数に設定する
  • ※アクセス権限はLambda、KMSともに設定してくだい

Lambdaの処理の流れ

  • ②で作成したKMSを用いて③のデータキーを復号し、平文の暗号化キーを取得
  • 画像データを取得
  • 平文の暗号化キーをもとに、画像データを暗号化
  • 暗号化した画像データをS3にPUT

ソースコード

lambda_function.py
import os
import json
import urllib.parse
import boto3
import base64
from Crypto.Cipher import AES # 補足①,②

print('Loading function')
s3_resource  = boto3.resource('s3')
kms = boto3.client('kms')

def lambda_handler(event, context):
    enc         = base64.b64decode(os.environ['CiphertextBlob'])
    decoded_key = kms.decrypt(CiphertextBlob = enc)
    data_key    = decoded_key['Plaintext']

    # S3にPUTされた画像を暗号化したかったので、eventから取得しています。
    bucket = event['Records'][0]['s3']['bucket']['name']
    key    = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')

    try:
        # 画像データの取得
        obj      = s3_resource.Object(bucket, key)
        response = obj.get()
        body     = response['Body'].read()

        # 補足③
        cipher          = AES.new(data_key, AES.MODE_EAX)
        ciphertext, tag = cipher.encrypt_and_digest(body)
        
        # 保存先
        cipher_key  = 'cipher/' + key
        tag_key     = 'tag/' + key
        nonce_key   = 'nonce/' + key
        cipher_obj  = s3_resource.Object(bucket,cipher_key)
        tag_obj     = s3_resource.Object(bucket,tag_key)
        nonce_obj   = s3_resource.Object(bucket,nonce_key)

        #保存
        cipher_obj.put( Body=ciphertext )
        tag_obj.put( Body=tag )
        nonce_obj.put( Body=cipher.nonce )

        #蛇足:復号するときは
        cipher_dec = AES.new(data_key, AES.MODE_EAX, cipher.nonce)
        dec_data   = cipher_dec.decrypt_and_verify(ciphertext, tag)
        

    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e

補足

補足①:暗号化ライブラリに何を用いるか

pycryptoと、pycryptodomeについての記事が多く見受けられました。
しかしpycryptoはバグが指摘されているまま更新がされていないらしく、現在(2021/3)は別のライブラリを用いたほうが良いとのことでした。
pycryptoからフォークしたpycryptodomeであれば、同じような使い方ができると思い、pycryptodomeを選択しました。

補足②:モジュールのimport

なにも考えずにfrom Crypto.Cipher import AESを書いてテストをすると下記のエラーが。

error.log
No module named 'Crypto'

外部モジュールだから存在しないよ、とのことでエラーになってしまいました。
こちらの記事を参考にしながら、ローカルでインストールしてzipファイルをLambdaにアップロードしました。

しかしそれでも下記のエラーに。

error.log
[ERROR] OSError: Cannot load native module 'Crypto.Cipher._raw_ecb': Trying '_raw_ecb.cpython-37m-x86_64-linux-gnu.so': 

エラーの原因がわからず、試しにpycyptoに変更しても同じくエラーに。

error.log
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': cannot import name '_AES' from 'Crypto.Cipher' (/var/task/Crypto/Cipher/__init__.py)

原因を調べている中で、Lambdaレイヤーに設定する方法にたどり着きました。

こちらの記事をもとに、Lambdaレイヤーにpycryptodomeを設定したら、動かすことができました!!

補足②:暗号化の実施

KMSのマスターキーを使ってデータの暗号化を試みたところ、内容が多すぎるとのエラーが。

error.log

Encrypt operation: HTTP content length exceeded 200000 byte

これまで何も考えずにマスターキーで暗号化をしていましたが、KMSにはデータキーというデータ暗号化用のキーが存在しており、マスターキーは暗号化出来るサイズが制限されているとのことでした。

※マスターキーを使うのが駄目、というわけではないみたいです。

データキーを取得するにはgenerate_data_keyを実行すればよく、下記のような形で返却されます。

kms_data_key.json

{
     "CiphertextBlob": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
     "KeyId": "arn:aws:kms:ap-northeast-1:012345678910:key/00000000-aaaa-1111-bbbb-222222222222",
     "Plaintext": "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
 }

CiphertextBlobというのが暗号化されたデータキーで、Plaintextが平文のデータキーです。
CiphertextBlobはbase64でencodeされているので、復号する際にはbase64でのデコードとKMSでのデコードが必要になります。
平文のデータキーは漏洩しないよう、保存などは行わずに破棄します。
今回はCiphertextBlobをLambdaの環境変数に設定し、Lambda内で復号して利用することにしました。

わかっていないこと

AES暗号化には暗号化のタイプが色々と用意されているみたいなのですが、それぞれ何が違うのか、どんなメリデメがあるのか、などがさっぱりわかっておりません...

参考にした記事

ありがとうございました。

3
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?