vault-railsを利用してカラムの暗号化・復号化を行う

# はじめに

HashiCorpからVaultとRailsを連携させるためのgem`vault-rails`が提供されていたのでサンプルアプリを作成して動作確認してみました。

# HashiCorp Vaultとは？
HashiCorpが提供している機密情報の管理ツールです。

# vault-railsとは？
HashiCorpから提供されているVaultとRailsを連携させるためのgemです。
データの暗号化・復号化を`vault-rails`が透過的に処理してくれるため、利用するデータの暗号化・復号化をあまり意識せずにコードを記述することができます。
データの暗号化・復号化に利用するKeyがVaultに格納され、暗号化したデータ自体はRailsのDBに格納されるようです。

# 参考
[公式サイト : Vault by HashiCorp](https://www.vaultproject.io/)
[Gihub : hashicorp/vault\-rails](https://github.com/hashicorp/vault-rails)

# バージョン

## Vault
v0.8.0

## Rails
rails(5.1.3)
vault (0.10.1)
vault-rails (0.3.2)

# Vault

## 起動
Vaultを起動して「127.0.0.1:8200」でHTTPリクエストを受信できる状態にしておく。
(unsealも忘れずに)

参考：[HashiCorp Vault で HTTP API を利用する \- Qiita](http://qiita.com/NaokiIshimura/items/4114af8cffa2361bcd19)

補足：
Vaultがunsealされてないとデータの読み込みが出来ず、以下のようなエラーが発生します。

```
The Vault server at `http://127.0.0.1:8200' responded with a 503.
Any additional information the server supplied is shown below:

  * Vault is sealed
```

## mount

`vault-rails`ではsecret backendとして`transit`を利用するので、マウントしておく。

```shell-session
$ vault mount transit
Successfully mounted 'transit' at 'transit'!
```

補足：
`transit`のマウントを忘れるとRailsでデータを読み書きする際に以下のようなエラーが発生します。

```
The Vault server at `http://127.0.0.1:8200' responded with a 404. 
Any additional information the server supplied is shown below: 
* no handler for route 'transit/encrypt/my_app_users_ssn//' 
* Please refer to the documentation for help.
```

# Rails

[hashicorp/vault\-rails](https://github.com/hashicorp/vault-rails)のREADMEを参考にアプリを準備していきます。

## Gemfile

```ruby:Gemfile
gem "vault-rails", "~> 0.1", require: false
```

## initializer

READMEのサンプルはProduction環境だけで動作させる内容になっていたので、develop環境でも利用できるように`vault.enabled`を書き換えました。`vault.address`や`vault.token`は環境変数でも指定できるようですが、今回は動作確認なのでベタ書きしました。

```ruby:config/initializers/vault.rb
require "vault/rails"

Vault::Rails.configure do |vault|
  # Use Vault in transit mode for encrypting and decrypting data. If
  # disabled, vault-rails will encrypt data in-memory using a similar
  # algorithm to Vault. The in-memory store uses a predictable encryption
  # which is great for development and test, but should _never_ be used in
  # production.
  #vault.enabled = Rails.env.production?
  vault.enabled = true

  # The name of the application. All encrypted keys in Vault will be
  # prefixed with this application name. If you change the name of the
  # application, you will need to migrate the encrypted data to the new
  # key namespace.
  vault.application = "my_app"

  # The address of the Vault server. Default: ENV["VAULT_ADDR"].
  vault.address = "http://127.0.0.1:8200"

  # The token to communicate with the Vault server.
  # Default: ENV["VAULT_TOKEN"].
  vault.token = "xxxxx-xxxxx-xxxxx-xxxxx-xxxxx"
end
```

## Model

READMEでは`person`でしたが、`user`でモデルを作成しました。
暗号化の対象はREADMEと同じく`ssn`とします。
暗号化したデータを格納する`ssn_encrypted`も合わせて用意します。

```shell-session
$ rails g scaffold user name:string ssn:string ssn_encrypted:string
$ rake db:migrate
```

```ruby:app/models/user.rb
class User < ApplicationRecord
  include Vault::EncryptedModel
  vault_attribute :ssn
end
```

# データの暗号化・復号化

http://localhost:3000/users/new にアクセスして、scaffoldで自動生成されたフォームからデータを入力します。`ssn_encrypted`には自動的にデータが挿入されるため、空のままにします。

![vault-rails3.png](https://qiita-image-store.s3.amazonaws.com/0/141934/5691ddfa-9edc-d9d0-049e-5b885ac908f3.png)

データの保存に成功すると`ssn_encrypted`に暗号化したデータが保存されます。

![vault-rails4.png](https://qiita-image-store.s3.amazonaws.com/0/141934/ee35fdb3-ccf8-562a-013c-9abc0acbadce.png)

画面を見た時に「あれ？入力した値が`ssn`に表示されてるぞ？暗号化されてないんじゃない？」と思いますが、テーブルをselectすると`ssn`カラムに実データが保存されてないことがわかります。

<img width="871" alt="rails-vault5.png" src="https://qiita-image-store.s3.amazonaws.com/0/141934/bf2f51c9-e101-e8a9-cdfd-fdf0461fd908.png">

`user.ssn`を呼び出す際に`rails-vault`が自動的にデータを復号化してくれていたようです。
データの暗号化・復号化を`vault-rails`が透過的に処理してくれるのは便利ですね。