How to safely store API keys in Rails apps
… and check why 5600+ Rails engineers read also this
How to safely store API keys in Rails apps
Inspired by a question on reddit: Can you store user API keys in the database? I decided to elaborate just a little bit on this topic.
Assuming you want store API keys (or passwords for SSL ceritifcate files) what are your options? What are the pros and cons in each case.
Save directly in codebase
How?
#config/environments/production.rb
config.mailchimp_api_key = "ABCDEF"
Cons:
- Won’t work with dynamic keys provided by users of your app
- Every developer working on your app knows API keys. This can bite you later when that person leaves or is fired. And I doubt you rotate your API keys regularly. That includes every notebook your developers have, which can be stolen (make sure it has encrypted disc) or gained access to.
- Every 3rd party app has access to this key. That includes all those cloud-based apps for storing your code, rating your code, or CIs running the tests. Even if you never have a leak, you can’t be sure they don’t have a breach in security one day. After all, they are very good target.
- Wrong server configuration can lead to exposing this file. There has been historical cases where attackers used
../../something/else
as file names, parameter names to read certain files on servers. Not that likely in Rails environment, but who knows. - In short: when the project code is leaked, your API key is leaked.
- Least safe
Save in ENV
How:
config.mailchimp_api_key = ENV.fetch('MAILCHIMP_API_KEY')
Pros:
- Won’t work with dynamic keys provided by users of your app
- Relatively easy. On Heroku you can configure production variables in their panel. For development and test environment you can use dotenv which will set environment based on configuration files. You can keep your development config in a repository and share it with your whole team.
Cons:
- If your
ENV
leaks due to a security bug, you have a problem.
Save in DB
How
class Group < ApplicationRecord
end
Group.create!(name: "...", mailchimp_api_key: "ABCDEF")
Pros
- Easy
- Works with dynamic keys
Cons
- If you ever send
Group
as json, via API, or serialize to other place, you might accidentally leak the API key as well. Take caution to avoid it. - If your database or database backup leaks, the keys leaks as well. This can especially happen when developers download backups or use them for development.
Save in DB and encrypt (secret in code or in ENV)
How
class Group < ApplicationRecord
attr_encrypted_options.merge!(key: ENV.fetch('ATTR_ENCRYPTED_SECRET'))
attr_encrypted :mailchimp_api_key
end
Group.create!(name: "...", mailchimp_api_key: "ABCDEF")
- use attr_encrypted
- and already mentioned dotenv
Pros
- For the sensitive API key to be leaked, two things needs to happen:
- DB leak
- ENV or code leak, which contain the secret you use for encryption
- If only one of them happens, that’s not enough.
- The safest approach
Cons
- A bit more complicated, but not much
- Your test might be a bit slower when you strongly encrypt/decrypt in most important models, which are used a lot
Use encrypted Rails secrets
How
- Configure
RAILS_MASTER_KEY
env variable on your development and production environment - Edit
config/secrets.yml.enc
usingbin/rails secrets:edit
and commit + push the changes - Set
config.read_encrypted_secrets = true
at least inconfig/environments/production.rb
- Use
Rails.application.secrets
in the application code - Read more
Pros
- Your API keys are encrypted
- The keys are versioned using your version control system such as GIT
Cons
- Does not work with dynamic keys
Would you like to continue learning more?
If you enjoyed the article, subscribe to our newsletter so that you are always the first one to get the knowledge that you might find useful in your everyday Rails programmer job.
Content is mostly focused on (but not limited to) Ruby, Rails, Web-development and refactoring Rails applications.