Introducing Portunus
Over the past few days, I've extracted Portunus into a standalone gem. Portunus is a KEK and DEK encryption gem that works in a similar manner to ActiveStorage. It provides database encryption for your data and is intended to be used with Ruby on Rails and ActiveRecord.
Portunus is (was?) the Roman god of keys, doors and ports. It continues my unoriginal naming scheme of ancient gods.
What is KEK and DEK?
KEK and DEK stands for "key encryption key" and "data encryption key". It is a way to encrypt your data that ensures separation of concerns. The data encryption key is responsible for directly encrypting the data (say an email address). That key is then encrypted with the "key encryption key". The encrypted data encryption key is then saved with the data.
Why a new gem?
I wanted a drop in KEK and DEK that took no thought and offered sensible secure defaults. It also needed to support key rotation without having to write custom tasks to do so. Other gems were taking too much set up and aren't aimed at junior developers. My goals were as follows:
- Set up in under 5 minutes with no chance of configuration errors
- Key rotation built in. This is always the part of encrypting data that is forgotten! Those keys need rotated!
- Customizable / extensible. Handle any use case and be extensible to organizations that require even higher security.
- High test coverage.
- Object oriented.
- Hashing and indexing support
- No extra dev work to utilize encryption.
Separation of concerns.
Bureaucracy is the enemy of fraud. The person writing checks should be different than the person that decides what amount the check is for. An immediate double check process is created by separating of the duties. Now instead of taking only one person to commit fraud, it takes two.
Insulating your master keys is a way that a company can attain data security. If the dev ops team responsible for managing master keys does not have database access, and those with database access don't have access to the master keys, no one should be able to view the data you hold without working together.
Portunus was built with HSM deployments in mind. An HSM or "hardware security module" is very similar to Ethan Hunts self destructing messages in Mission Impossible series. It is a specially designed enclosure that prevents tampering and wipes data when unauthorized access is detected. You will need to write the connection yourself since each deployment typically varies.
Do I need KEK and DEK?
The benefits for KEK and DEK are the organizational boundaries put in place by separating responsibilities. Most likely in the beginning of your company, these checks probably are not required.
But... this isn't about the simplest way to build things for a developer. It's about the simplest way to build a SaaS company. Getting DEK and KEK set up early is going to save tremendous headaches later from having to encrypt millions or billions of rows of data. Being able to monitor key rotation times from the beginning is going to save you from breaching your SLA and ISO policies.
Portunus makes this structure available in minutes. If you wait to implement your encryption it will most likely takes months to transition your database to something that is fully encrypted.
How does it work?
Portunus can be dropped into any Rails project with little to no configuration. It stores all the data encryption keys into its own data encryption key table like ActiveStorage. Only 1 auto generated migration is all that is needed to get started.
Encrypters are classes that are responsible for encrypting the data. Portunus utilizes OpenSSL by default to do the encryption. You can configure an encryption engine in the initialization or write your own. You can write a custom one to send the data to your HSM if you need to decrypt it there (due to externally stored master keys).
Storage Adaptors are responsible for looking up and assigning master keys. Portunus comes with two storage adaptors for the most common secret managements deployments.
- Environment - for storing keys in the environment directly
- Credentials - work with Rails built in credentials system (default)
External HSM deployments could use either. Instead of storing your keys themselves in the credentials or environment you would store the key id's. Using the environment makes it pretty easy in that scenario to script adding new master keys and expiring old ones automatically.
Casting
Encrypted data is stored as strings. Portunus has built in casting support to make it seamless when working with your data. You can specify on a per column basis the type that is required to be returned.
Casting behavior is also fully customizable if there's different preferences for how default data is returned.
Hashing / Blind indexing support
Encrypted data isn't searchable. However if we hash the data and store it in a seperate column we can then at least lookup based on the hash. This is called a blind index. In order to support this if Portunus detects a field prefixed with hashed_ it will store a hashed value prior to encrypting it.
Key rotation
Encrypting data is a good first step. But people come and go from the company, computers get stolen, and life happens. The longer the keys are in use, the less secure they become. Rotating your keys for your existing data combats this.
Certain company events can trigger a key rotation. These can be things like a developer leaving the company, a data breach of the database and possible theft from the office to list just a few.
Portunus includes two rake tasks for rotating your keys. One rotates the DEKs and the other will rotate the KEKs. If these are scheduled via cron they will ensure that your data does not have keys older than your specified max key length.
Check it out
More documentation and guides are available on Github. Check out the source code below:
Security disclaimer.
The goal with Portunus is to be more secure than by not doing anything. I wrote Portunus with a specific security objective in mind. In the event of a database leak or unauthorized access, the data inside is secure and not exposed and mostly useless.
Security is layers and this is simply one additional layer and not a complete security solution. This data being encrypted may reside unencrypted in logs or other databases such as Redis or Elasticsearch.
Code examples:
To install:
gem "portunus"
Configuration:
Portunus.configure do |config|
config.encrypter = MyCustomEncryptionClass
config.storage_adaptor = MyCustomStorageClass
end
To encrypt a field:
class User < ApplicationRecord
encrypted_fields :email, :name
end
Customize field options
class User < ApplicationRecord
encrypted_fields :email, :name, age: { type: :integer }
end
Rotate Tasks
bundle exec rake portunus:rotate_keks
bundle exec rake portunus:rotate_deks
- Note: With rotating KEK's, new keys need to be manually inserted into the environment and the old ones disabled. This might be programmable depending on your storage adaptor.
Other last notes:
- Use as many master keys as possible while still being convienant. Using a single master key is going to lead to long rotation times. If you use 10 master keys you can rotate 1/10th of your data easily and in parallel. Depending on the size of your data there may be hundreds or thousands master keys. Most SaaS apps I encounter can use 5-10 to keep things reasonable and sane. At some point the injection / expiration of new master keys will have to get automated. It also helps to ensure that IV's do not get repeated in larger data sets.
- If you run into any bugs / issues let me know via Github issues.
- It still requires some battle testing and useage in different types of projects.