Strongbox by Optimis provides Public Key Encryption for ActiveRecord. By using a public key
sensitive information can be encrypted and stored automatically. Once stored a
password is required to access the information. More importantly beyond the iniial
scope of Strongbox, Strongbox by Optimis provides for decryption by a remote server,
thus providing a machine separation between the public and private keys. This
was implemented to provide the best possible security implementation to comply
with federal HIPAA standards. If you are going to use the remote decryption
functionality, you’ll need to also install the RestClient gem.
It is strongly recommended that unless you know exactly what you’re doing,
you should use http://github.com/spikex/strongbox instead.
Because the largest amount of data that can practically be encrypted with a public
key is 245 byte, by default Strongbox uses a two layer approach. First it encrypts
the attribute using symmetric encryption with a randomly generated key and
initialization vector (IV) (which can just be through to as a second key), then it
encrypts those with the public key.
Strongbox stores the encrypted attribute in a database column by the same name, i.e.
if you tell Strongbox to encrypt “secret” then it will be store in “secret” in the
database, just as the unencrypted attribute would be. If symmetric encryption is used
(the default) two additional columns “secret_key” and “secret_iv” are needed as well.
The attribute is automatically encrypted simply by setting it:
user.secret = “Shhhhhhh…”and decrypted by calling the “decrypt” method with the private key password.
plain_text = user.secret.decrypt ‘letmein’If the remote decryption service is configured, the data can be accessed via
plain_text = user.secret.decrypt_remotelyNote: This will make a request to the remote service with RestClient.
In your model:
class User < ActiveRecord::Base
encrypt_with_public_key :secret,
:key_pair => File.join(RAILS_ROOT,'config','keypair.pem') (optional if using remote decryption service)
:public_key => <path to public portion of the key>
:api_key <path to shared key for signature with remote decryption service>
:decryption_service_url <server address of decryption service like "http://decrypt.mydomain.com/decrypt_field">
end
In your migrations:
class AddSecretColumnsToUser < ActiveRecord::Migration
def self.up
add_column :users, :secret, :binary
add_column :users, :secret_key, :binary
add_column :users, :secret_iv, :binary
end
def self.down
remove_column :users, :secret
remove_column :users, :secret_key
remove_column :users, :secret_iv
end
end
Generate a key pair:
(Choose a strong password.)
openssl genrsa -des3 -out config/private.pem 2048
openssl rsa -in config/private.pem -out config/public.pem -outform PEM -pubout
cat config/private.pem config/public.pem >> config/keypair.pem
In your views and forms you don’t need to do anything special to encrypt data. To
decrypt call:
user.secret.decrypt 'password'
When using this gem with a remote decryption service, the private key generated
from the above process should be explicitily removed from the application server
and instead installed on the remote decryption server. Furthermore, the keypair
pass phrase should also not be stored on the app server but instead be placed on
the remote decryption server.
In config/environment.rb:
config.gem "strongbox"
encrypt_with_public_key sets up the attribute it’s called on for automatic
encryption. It’s simplest form is:
class User < ActiveRecord::Base
:key_pair => File.join(RAILS_ROOT,'config','keypair.pem') (optional if using remote decryption service)
:public_key => <path to public portion of the key>
:api_key <path to shared key for signature with remote decryption service>
:decryption_server <server address of decryption service>
end
Which will encrypt the attribute “secret”. The attribute will be encrypted using
symmetric encryption with an automatically generated key and IV encrypted using the
public key. This requires three columns in the database “secret”, “secret_key”, and
“secret_iv” (see below).
Options to encrypt_with_public_key are:
:public_key – Path to the public key file. Overrides :keypair.
:private_key – Path to the private key file. Overrides :keypair.
:keypair – Path to a file containing both the public and private keys.
:api_key – Path to the key required to sign requests to the decryption sevice
:decryption_server – the address to the decryption server.
:symmetric :always/:never – Encrypt the date using symmetric encryption. The public
key is used to encrypt an automatically generated key and IV. This allows for large
amounts of data to be encrypted. The size of data that can be encrypted directly with
the public is limit to key size (in bytes) – 11. So a 2048 key can encrypt 245 bytes. Defaults to :always
:symmetric_cipher – Cipher to use for symmetric encryption. Defaults to ‘aes-256-cbc’. Other ciphers support by OpenSSL may be used.
:base64 true/false – Use Base64 encoding to convert encrypted data to text. Use when
binary save data storage is not available. Defaults to false
:padding – Method used to pad data encrypted with the public key. Defaults to
RSA_PKCS1_PADDING. The default should be fine unless you are dealing with legacy
data.
For example, encrypting a small attribute, providing only the public key for extra
security, and Base64 encoding the encrypted data:
class User < ActiveRecord::Base
validates_length_of :pin_code, :is => 4
encrypt_with_public_key :pin_code,
:symmetric => :never,
:base64 => true,
:public_key => File.join(RAILS_ROOT,'config','public.pem')
end
Generate a key pair:
openssl genrsa -des3 -out config/private.pem 2048
Generating RSA private key, 2048 bit long modulus
......+++
.+++
e is 65537 (0x10001)
Enter pass phrase for config/private.pem:
Verifying - Enter pass phrase for config/private.pem:
and extract the the public key:
openssl rsa -in config/private.pem -out config/public.pem -outform PEM -pubout
Enter pass phrase for config/private.pem:
writing RSA key
If you are going to leave the private key installed it’s easiest to create a single
key pair file:
cat config/private.pem config/public.pem >> config/keypair.pem
Or, for added security, store the private key file else where, leaving only the public key.
In it’s default configuration Strongbox requires three columns, one the encrypted
data, one for the encrypted symmetric key, and one for the encrypted symmetric IV. If
symmetric encryption is disabled then only the columns for the data being encrypted
is needed.
If your underlying database allows, use the binary column type. If you must store
your data in text format be sure to enable Base64 encoding and to use the text
column type. If you use a string column and encrypt anything greater than 186 bytes (245 bytes if you don’t enable Base64 encoding) your data will be lost.
If you don’t encrypt your data, then an attacker only needs to steal that data to get
your secrets.
If encrypt your data using symmetric encrypts and a stored key, then the attacker
needs the data and the key stored on the server.
If you use public key encryption, the attacker needs the data, the private key, and
the password. This means the attacker has to sniff the password somehow, so that’s
what you need to protect against.
If you use a remote decryption service, an attacker needs to gain access to both
the app server AND the decryption server.
Spike Ilacqua, Clark Snowdall, Ehren Murdick
Strongbox’s implementation drew inspiration from Thoughtbot’s Paperclip gem
http://www.thoughtbot.com/projects/paperclip