By

Amazon KMS を利用した 秘密情報管理 ruby ライブラリ Voynich

先日、秘密情報暗号化を管理する ruby ライブラリ Voynich を公開しました。
本記事では、Voynich の使い方と開発に至った背景を紹介します。

Voynich とは

Voynich は 鍵管理に Amazon KMS (Key Management Service) を利用した秘密情報管理 ruby ライブラリです。ActiveRecord をデータの保存先として利用するため、基本的には Rails アプリケーションでのデータ暗号化に用いることを想定しています。データの暗号化には AES-GCM-256 を用いており、データの認証にも対応しています。

なぜ Voynich を開発したのか

Web アプリケーションは常にセキュリティ的な脅威に直面しているので、DB に保存されているデータはそのうち流出すると仮定すべきです。
Degica で運用するサービス には EC プラットフォームや決済サービス Komoju 等があり、保存するデータの一部は厳格なセキュリティ管理が必要です。とりわけクレジットカード情報の保存は業界標準の PCI DSS により非常に高レベルな要求が定められています。そのような秘密データの管理をしてくれるライブラリやサービスをいくつか調べたのですが、残念ながら我々の要求に合致するものをみつけられなかったため、Voynich の開発を開始しました。

Voynich が提供する機能は大きく分けると「秘密情報の安全な保存」と「鍵の管理」です。このうち、「秘密情報の安全な保存」は難しい問題ではありません。Ruby が OpenSSL ライブラリを提供しているのでそれを使って暗号化すればよいです。厄介なのは「鍵の管理」の方なので、以下で少し詳しく説明します。

鍵管理の難しさ

どのような暗号化方式を利用するにしても、暗号化には必ず秘密鍵が必要です。暗号化されたデータ自体は (暗号化ライブラリ/アルゴリズムに脆弱性が発見されないかぎりは) 攻撃に対して安全といえますが、一方で秘密鍵が流出してしまったら暗号化されているデータがすべて盗まれてしまう可能性があります。
暗号化は、すべてのデータを安全に保存する代わりに秘密鍵だけを機密情報として扱うことであるといえますが、暗号化のアルゴリズムやライブラリは鍵の管理をどうすればよいか教えてくれません。
「鍵をどこにどうやって保存するか」というのは非常に難しい問題です。もし安全に鍵を保存できる場所が存在するのであればデータ自体をそこに保存すればよいので、そもそも暗号化は必要ありません。

例として、以下で Hashicorp VaultAmazon KMS がどのように鍵を管理するかを説明します。

Hashicorp Vault の鍵管理

Hashicorp の Vault では Seal/Unseal という面白い仕組みを用いてマスター鍵の管理をしています。 詳細は公式ドキュメントに譲りますが、簡単に説明するとマスター鍵を複数のデータ (シャード) に分割して 起動時にその複数のシャードを Vault に渡すことでマスターキーを再構築します。各シャードは例えば開発者 1 人に 1 つずつ渡しておいて、起動時にそれぞれの開発者がシャードを入力するような運用にして管理します。

Vault はこのような仕組みを用いることで「マスター鍵がどこにも保存されていない状態」を実現しています。

Vault の鍵管理の仕組みは正しく使えばとても安全ですが、ひとつ問題があります。Vault の起動を自動化できないことです。Vault を起動する際は必ず人の手でシャードを入力する必要があるため、例えばサーバーをリプレイスするときはシャード所有者にシャードを入力してもらわなければなりません。もちろんシャード自体をどこかに保存すれば起動を自動化するスクリプトを作ることはできますが、それではそのシャードはどうやって安全に保存するのか、という最初の問題に戻ってしまいます。

Degica でもはじめは Vault の導入を検討していたのですが、この問題を解決することができず、Vault 導入は断念しました。

Amazon KMS (Key Management Service)

Amazon KMS は AWS が提供するサービスの一つで、データ暗号化/復号化の機能を提供しています。KMSのマスター鍵(Customer Master Key, CMK) は AWS データセンター内の HSM (Hardware Security Module) に安全に保存されています。 CMK は KMS の外には出て行かないので、利用者も CMK の中身を知ることはできません。CMK はデータセンター内に保存されているため流出のリスクはゼロではありませんが (Amazon を信頼するなら) 自前の保存ソリューションを構築するよりは流出の危険性は極めて低いと言えるでしょう。

KMS を用いることで前述の Vault の問題点を回避することができますが、Vault が包括的な秘密情報管理サービスであるのに対して、KMS は暗号化と復号化の機能のみを提供しているため暗号化データの保存に関しては別に実装する必要があります。Voynich は鍵管理にKMS を用いて暗号化データの保存機能を提供するライブラリです。

Voynich の使い方

インストール

次のように Gemfile に記述して Voynich をインストールします。

gem 'voynich'
$ bundle install

マイグレーション

Voynich を利用するには 2 つのテーブルが必要です。以下のジェネレータを実行してマイグレーションファイルを作成してください。

$ rails g voynich:active_record
$ rake db:migrate

これで、voynich_data_keysvoynich_values というテーブルが作成されます。

設定

次のようにして、Voynich の設定を実施します。

Voynich.configure(
  aws_access_key_id: 'aakid',
  aws_secret_access_key: 'asak',
  kms_cmk_id: 'cmk_id',
  aws_region: 'ap-northeast-1'
)

AWS のアクセスキーと CMK は事前に AWS に作成しておいてください。

ActiveModel インテグレーション

Voynich は Ruby on Rails で動作することを前提にしているため、もっとも簡単な利用方法は ActiveModel インテグレーションを利用することです。

$ rails g voynich:model_attribute YourModel secret_data

上記のジェネレータを実行すると、YourModel モデルの secret_data 属性を暗号化するための設定が生成されます。

### db/migrate/xxxxxx_add_voynich_secret_data_value_id_to_your_models.rb
+class AddVoynichCreatedAtValueIdToYourModels < ActiveRecord::Migration
+  def change
+    add_column :your_models, :voynich_secret_data_value_id, :integer
+  end
+end
### app/models/your_model.rb
 class YourModel < ActiveRecord::Base
+  include Voynich::ActiveModel::Model
+  voynich_attribute :secret_data
 ## ...

あとは通常のように secret_data にアクセスすることができます。

model = YourModel.new
# 生データのアサイン
model.secret_data = {card_number: '1234567890123456'}

# 保存すると Voynich がデータを暗号化して保存する
model.save

# 通常のモデル属性と同じように生データにアクセスできる
# 実際には getter 内で復号化処理を実施しています
model.secret_data # => {card_number: '1234567890123456'}

# Voynich が管理している `Value` モデルにアクセスすることもできます
model.voynich_secret_data_value
# => #<Voynich::ActiveRecord::Value id: 1, data_key_id: 1, uuid: "...", ciphertext: "{\"c\":\"chD9hCWePs+Cqg...">

鍵のローテーション

Voynich が利用・生成する鍵は 2 種類あります。ひとつめは KMS の CMK で、これは後述する DEK の暗号化にのみ用いられます。
2 つめがデータ暗号化キー (Data Encryption Key, DEK) で、この鍵は KMS によりランダムに生成され、CMK によって暗号化されます。暗号化された DEK は Voynich によりアプリケーションの DB に保存されます。

これらの鍵は定期的に更新することがセキュリティのベストプラクティスとして知られています。KMS は CMK のローテーションに対応しており、有効化すると 1 年に 1 回 CMK を更新します。
Voynich は現状は DEK のローテーションには対応していませんが、近いうちに実装する予定です。

データの認証

Voynich は データの暗号化に AES-GCM-256 を使用しています。これはカウンターモードの AES による暗号化に加えてデータの認証を実施する暗号化モードです。GCM は authentication tag と authentication data の 2 種類の認証をサポートしています。このうち authentication tag は Voynich 内部で適切に設定されるため利用者が気にする必要はありませんが、authentication data はアプリケーション側のコンテキストを暗号化/復号化の追加情報として指定する機能なので、利用するにはアプリケーションから authentication data を渡さなければなりません。

Voynich では authentication data を “context” と呼んでおり、次のようにして context を指定することができます。

class YourModel < ActiveRecord::Base
   include Voynich::ActiveModel::Model
   voynich_attribute :secret_data, context: -> (m) { {uuid: m.uuid} }
 ## ...

例では uuid というモデル属性を context に使いましたが、アプリケーションの要求に応じて設定する値を決定してください。

AWS アクセスキーの管理

AWS アクセスキーが流出すると、もし攻撃者が暗号化された DEK とデータを入手できればデータを復号できてしまいます。KMS の使用履歴はすべて記録されること、AWS アクセスキーはいつでも無効化できることからマスター鍵が流出することに比べれば幾分ましではありますが、EC2 インスタンスロール等を用いてアクセスキーが流出しないようにすることが望ましいです。

まとめ

データの暗号化と同様に、鍵の管理を正しく実装することは非常に重要なことですが、鍵の管理まで行ってくれるライブラリは少ないです。
AWS と Rails を使っているサービスであれば Voynich はデータ暗号化の有力な選択肢になると思います。

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