By

AWS Key Management Service を使ってデータの暗号化をする

3/11に開催されたKichijoji.rb #1で AWS Key Management Service を使ってデータ暗号化について発表しました。

今回はこの発表内容を解説します。

クレジットカードデータは非常に慎重に取り扱う必要があります。
データの取扱いについての指針となる資料がPCI DSSです。

https://www.pcisecuritystandards.org/documents/PCI_DSS_v3.pdf

このドキュメントはクレジットカードを取り扱う事業者が守るべき技術面及び運用面での最低限の要件を定めたものです。
PCI DSS が定める要件は多岐に渡ります。詳細はドキュメントに譲るとして、ここではクレジットカードの保存に関わる部分について、PCI DSSの要件を満たすサービスをどのように設計すればよいか、Amazon KMS を利用した例を紹介します。

PCI DSSの要件

3.4 Render PAN unreadable anywhere it is stored (including on portable digital media, backup media, and in logs) by using any of the following approaches:
- One-way hashes based on strong cryptography, (hash must be of the entire PAN)
- Truncation (hashing cannot be used to replace the truncated segment of PAN)
- Index tokens and pads (pads must be securely stored)
- Strong cryptography with associated key-management processes and procedures.

3.4章ではクレジットカードデータをどのように保存すべきかを定めています。ワンウェイハッシュによるハッシュ化、カードデータの切り捨て、インデックストークンを使う、強力な暗号化手法を用いた暗号化のいずれかにより保存されるデータを読み取り不能にしなければなりません。今回紹介するのは「強力な暗号化手法による暗号化」です。

3.5.2 Store secret and private keys used to encrypt/decrypt cardholder data in one (or more) of the following forms at all times:
- Encrypted with a key-encrypting key that is at least as strong as the data-encrypting key, and that is stored separately from the dataencrypting key
- Within a secure cryptographic device (such as a host security module (HSM) or PTS-approved point-of-interaction device)
- As at least two full-length key components or key shares, in accordance with an industry

3.5.2章ではデータ暗号化をする鍵の保存について定めています。今回対象になるのは一番上の「データを暗号化する鍵はその鍵と最低でも同程度の強度の鍵で暗号化され、それぞれの鍵は別の場所に保存されなければならない」というものです。

AWS Key Management Service (KMS)

上記の要件を満たすために、AWS KMS を利用して暗号化と保存を行います。
KMS は AWS のサービスのひとつで、鍵の管理を行ってくれるサービスです。KMS を使うことで鍵の管理を簡単かつ安全に実施することができます。

KMS の詳細は公式の Developer Guide を参照してください。

CMK(Customer Master Keys)

KMS は CMK と呼ばれるマスターキーを使用して暗号化を行います。この CMK は基本的に KMS によって管理されるものなので鍵の内容を知る必要はありません。

データキーの生成と暗号化/復号化

KMS にはいくつかのAPIリソースがありますが、今回使用するのはgenerate_data_keydecryptです。
generate_data_key API にCMKのIDを渡してコールすると、KMSが新しいデータキーを生成して平文バージョンと CMK によって暗号化されたバージョンの2種類を返してくれます。また、decrypt API に先の暗号化されたバージョンのデータキーを渡すと KMS 内で CMK を用いて復号した平文バージョンのデータキーを返してくれます。
これらが実際どのように動くのか、ruby のサンプルコードを以下に載せます。

client = Aws::KMS::Client.new

### Generating data key
resp = client.generate_data_key(key_id: "c4daa226-...", key_spec: "AES_256")
Base64.encode64(resp.plaintext)
#=> "m4Qi66NvmLlV5ut8Qxiw/qA1q0vt2yadjqp6YhHthmA=\n"
encrypted_data_key = Base64.encode64(resp.ciphertext_blob)
#=> "CiDFJioCMK4fkTzvs2KVLh737rxUi3bT0GsQkCpsiT3nxhKnAQEBAwB4xSYq\nAjCuH5E87..."

### Decrypting encrypted data key
resp2 = client.decrypt(ciphertext_blob: Base64.decode64(encrypted_data_key))
Base64.encode64 resp2.plaintext
#=> "m4Qi66NvmLlV5ut8Qxiw/qA1q0vt2yadjqp6YhHthmA=\n"

パーミッション

KMSでは、他のAWSのAPIと同様にIAMを用いてリソース毎にパーミッションを設定することができるので、例えば generate_data_key API しか実行できないアクセスキーを作ることができます。設定の方法は下記のガイドを参照してください。

http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html

KMSを用いたサービスの設計

一般的なWebサービスを想定して、KMS を使ったシステムを考えます。
まず、外部からのリクエストを受ける Webサーバーと、実際にクレジットカードデータを使って決済処理を行う決済サーバーに役割をわけます。

Webサーバー

Webサーバーはリクエストを起点に generate_data_key API を使用して、クレジットカードデータの暗号化を行います。そして暗号化されたクレジットカードデータと暗号化に用いたデータキーの暗号化バージョンをセットで DB に保存します。つまり、この方法では各クレジットカードデータ毎に異なる鍵を使用して暗号化します。

決済サーバー

決済サーバーは何らかのトリガーで処理を開始して (Webサーバーからのリクエストや cron による定期実行を想定しています)、DB から暗号化されたクレジットカードデータと鍵を取り出します。次に decrypt API を使って取得した暗号化データキーを復号します。そして最後に復号化された平文のデータキーを用いてクレジットカードデータを復号します。

どちらのサーバーも平文バージョンのデータキーは使い終わったら保存することなく破棄します。
このように役割を分けることでWebサーバーはクレジットカードを復号しなくなりますので、generate_data_key 以外のパーミッションをWebサーバーから取り外すことができます。仮にWebアプリケーションに脆弱性が存在してサーバーを乗っ取られたとしても、Webサーバーには decrypt のパーミッションがありませんので攻撃者はデータを盗むことができません。一方で決済サーバーは外部からのリクエストを受けないので、乗っ取られる可能性はWebサーバーと比較して低いと言えます。

上記の設計にすることで

  • クレジットカードデータは暗号化されている
  • 暗号化に使用した鍵は KMS により暗号化されている
  • それぞれの鍵は別の場所に保存されている(データキーは DB に、CMK は KMS に保存されている)

となるので、PCI DSS の 3.4 と 3.5.2 を満たし、さらにWebサーバーはデータ復号化が不可能になるためさらなるセキュリティを確保することができました。

一緒にユニークな決済サービスを作ってくれる Rails エンジニアを募集中です!
多国籍なメンバーと一緒に仕事をしてみませんか?詳細はこちらのリンクです:D