README.md in kms_encrypted-0.1.2 vs README.md in kms_encrypted-0.1.3

- old
+ new

@@ -1,17 +1,19 @@ # KMS Encrypted -[KMS](https://aws.amazon.com/kms/) + [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted) +Simple, secure key management for [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted) The attr_encrypted gem is great for encryption, but: 1. Leaves you to manage the security of your keys -2. Doesn’t provide a great audit trail to see how data has been accessed +2. Doesn’t provide an easy way to rotate your keys +3. Doesn’t have a great audit trail to see how data has been accessed +4. Doesn’t let you grant encryption and decryption permission separately -KMS addresses both issues and it’s easy to use them together. +[KMS](https://aws.amazon.com/kms/) addresses all of these issues and it’s easy to use them together. -**Note:** This has not been battle-tested in a production environment, so use with caution +[![Build Status](https://travis-ci.org/ankane/kms_encrypted.svg?branch=master)](https://travis-ci.org/ankane/kms_encrypted) ## How It Works This approach uses KMS to manage encryption keys and attr_encrypted to do the encryption. @@ -25,22 +27,32 @@ ```ruby gem 'kms_encrypted' ``` -Add a column to store encrypted KMS data keys +Add columns for the encrypted data and the encrypted KMS data keys ```ruby -add_column :users, :encrypted_kms_key, :string +add_column :users, :encrypted_email, :text +add_column :users, :encrypted_email_iv, :text +add_column :users, :encrypted_kms_key, :text ``` +Create an [Amazon Web Services](https://aws.amazon.com/) account if you don’t have one. KMS works great whether or not you use other AWS services. + Create a [KMS master key](https://console.aws.amazon.com/iam/home#/encryptionKeys) and set it in your environment ([dotenv](https://github.com/bkeepers/dotenv) is great for this) ```sh KMS_KEY_ID=arn:aws:kms:... ``` +You can also use the alias + +```sh +KMS_KEY_ID=alias/my-alias +``` + And update your model ```ruby class User < ApplicationRecord has_kms_key @@ -135,20 +147,26 @@ Change the last line to point to your CloudTrail log bucket and query away ```sql SELECT eventTime, - eventName, userIdentity.userName, requestParameters FROM cloudtrail_logs WHERE eventName = 'Decrypt' + AND resources[1].arn = 'arn:aws:kms:...' ORDER BY 1 ``` +There will also be `GenerateDataKey` events. + +## Alerting + +We recommend setting up alerts on suspicious behavior. + ## Key Rotation KMS supports [automatic key rotation](http://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html). No action is required in this case. To manually rotate keys, replace the old KMS key id with the new key id in your model. Your app does not need the old key id to perform rotation (however, the key must still be enabled in your AWS account). @@ -163,17 +181,71 @@ User.find_each do |user| user.rotate_kms_key! end ``` +## IAM Permissions + +A great feature of KMS is the ability to grant encryption and decryption permission separately. + +To encrypt the data, use a policy with: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "EncryptData", + "Effect": "Allow", + "Action": "kms:GenerateDataKey", + "Resource": "arn:aws:kms:..." + } + ] +} +``` + +If a system can only encrypt, you must clear out existing data keys before updates. + +```ruby +user.encrypted_kms_key = nil # before user.save +``` + +To decrypt the data, use a policy with: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "DecryptData", + "Effect": "Allow", + "Action": "kms:Decrypt", + "Resource": "arn:aws:kms:..." + } + ] +} +``` + +Be extremely selective of systems you allow to decrypt. + +## Testing + +For testing, you can prevent network calls to KMS by setting: + +```sh +KMS_KEY_ID=insecure-test-key +``` + ## Multiple Keys Per Record You may want to protect different columns with different data keys (or even master keys). To do this, add more columns ```ruby -add_column :users, :encrypted_kms_key_phone, :string +add_column :users, :encrypted_phone, :text +add_column :users, :encrypted_phone_iv, :text +add_column :users, :encrypted_kms_key_phone, :text ``` And update your model ```ruby