Lockbox
:lock: File encryption for Ruby and Rails
Supports Active Storage and CarrierWave
Uses AES-GCM by default for authenticated encryption
Installation
Add this line to your application’s Gemfile:
gem 'lockbox'
Key Generation
Generate an encryption key.
SecureRandom.hex(32)
Store the key with your other secrets (typically Rails secrets or an environment variable).
Alternatively, you can use a key management service to manage your keys.
Files
Create a box
box = Lockbox.new(key: key)
Encrypt
box.encrypt(File.binread("license.jpg"))
Decrypt
box.decrypt(File.binread("license.jpg.enc"))
Active Storage
Add to your model:
class User < ApplicationRecord
has_one_attached :license
attached_encrypted :license, key: key
end
Works with multiple attachments as well.
class User < ApplicationRecord
has_many_attached :documents
attached_encrypted :documents, key: key
end
There are a few limitations to be aware of:
- Metadata like image width and height are not extracted when encrypted
- Direct uploads cannot be encrypted
CarrierWave
Add to your uploader:
class LicenseUploader < CarrierWave::Uploader::Base
encrypt key: key
end
Encryption is applied to all versions after processing.
Serving Files
To serve encrypted files, use a controller action.
def license
send_data @user.license.download, type: @user.license.content_type
end
Use read
instead of download
for CarrierWave.
Key Rotation
To make key rotation easy, you can pass previous versions of keys that can decrypt.
Lockbox.new(key: key, previous_versions: [{key: previous_key}])
For Active Storage use:
class User < ApplicationRecord
attached_encrypted :license, key: key, previous_versions: [{key: previous_key}]
end
To rotate existing files, use:
user.license.rotate_encryption!
For CarrierWave, use:
class LicenseUploader < CarrierWave::Uploader::Base
encrypt key: key, previous_versions: [{key: previous_key}]
end
To rotate existing files, use:
user.license.rotate_encryption!
Algorithms
AES-GCM
The default algorithm is AES-GCM with a 256-bit key. Rotate the key every 2 billion files to minimize the chance of a nonce collision.
XChaCha20
Install Libsodium and add rbnacl to your application’s Gemfile:
gem 'rbnacl'
Then pass the algorithm
option:
# files
box = Lockbox.new(key: key, algorithm: "xchacha20")
# Active Storage
class User < ApplicationRecord
attached_encrypted :license, key: key, algorithm: "xchacha20"
end
# CarrierWave
class LicenseUploader < CarrierWave::Uploader::Base
encrypt key: key, algorithm: "xchacha20"
end
Make it the default with:
Lockbox. = {algorithm: "xchacha20"}
You can also pass an algorithm to previous_versions
for key rotation.
Key Management
You can use a key management service to manage your keys with KMS Encrypted.
For Active Storage, use:
class User < ApplicationRecord
attached_encrypted :license, key: :kms_key
end
For CarrierWave, use:
class LicenseUploader < CarrierWave::Uploader::Base
encrypt key: -> { model.kms_key }
end
Note: KMS Encrypted’s key rotation does not know to rotate encrypted files, so avoid calling record.rotate_kms_key!
on models with file uploads for now.
Reference
Pass associated data to encryption and decryption
box.encrypt(, associated_data: "bingo")
box.decrypt(ciphertext, associated_data: "bingo")
History
View the changelog
Contributing
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- Report bugs
- Fix bugs and submit pull requests
- Write, clarify, or fix documentation
- Suggest or add new features