This blog post aims to lay out a simple and concrete strategy for handling sensitive data in your Ruby On Rails applications, and to explain the importance of such a strategy.
Never, ever check them into source control
Even if your project is closed source and your trusted colleagues are the only ones with access, you never know when a freelancer or consultant might be joining the project. Even if that never occurs, how do you keep track of all the locations where that repository is checked out? Who knows on how many hard drives your company’s credit card transaction secret API key might be stored. What happens when someone with a weak login password forgets their laptop on the bus or at the airport?
Also note that it’s not always as simple as removing secrets after the fact, especially with version control. It’s usually impossible to do this without drastically changing your entire project’s history!
Do it right
For a long time, we’ve been using YAML files to store our application configuration. It’s easy to manage and can be configured for different Rails environments. These YAML files could look like the following:
config/app.yml:
development: &defaults
awesomeness_score: 3
host: "localhost:3000"
s3_bucket: "example-development-us"
production:
<<: *defaults
host: "example.com"
s3_bucket: "example-production-us"
test:
<<: *defaults
config/app_secret.yml.example:
development: &defaults aws_access_key_id: "" aws_secret_access_key_id: ""
production:
<<: *defaults
test:
<<: *defaults
config/app_secret.yml:
development: &defaults
aws_access_key_id: "ACTUAL-ID-WOULD-GO-HERE"
aws_secret_access_key_id: "ACTUAL-SECRET-WOULD-GO-HERE"
production:
<<: *defaults
test:
<<: *defaults
Only the first two files would be checked in to source control, and the application’s README would instruct developers to cp config/app_secret.yml.example config/app_secret.yml and fill in the gaps from the company keychain.
To make sure we never check in the secrets by mistake, we ignore the app_secret.yml file:
.gitignore:
# ...
/config/app_secret.yml
We then use the econfig gem written by Jonas Nicklas to easily merge them together:
Gemfile
# ...
gem "econfig", require: "econfig/rails"
config/application.rb
# ...
module YourApp
extend Econfig::Shortcut
# ...
end
Now we can access any configuration variable and secret credential:
YourApp.host # => "localhost:3000"
YourApp.aws_secret_access_key_id # => "ACTUAL-SECRET-WOULD-GO-HERE"
Deploy
When you deploy the application, you must manually manage the secrets on the server(s).
Capistrano
If you deploy with Capistrano, you’ll want to place the app_secret.yml in your /shared folder. Once that’s done, it can be copied to each release with symlink task:
deploy.rb
# ...
namespace :config do
desc "Symlink application config files."
task :symlink do
run "ln -s {#{shared_path},#{release_path}}/config/app_secret.yml"
end
end
after "deploy", "config:symlink"
Heroku
If you’re deploying your application where you don’t have file access, such as Heroku, you’re better off storing this kind of information in ENV. The econfig gem has built in support for this and a few other storage backends, but that’s another blog post.
Conclusion
With this method, we now have a clear separation of sensitive and non-sensitive data. There’s no risk of checking in any sensitive data, since we have only one place to put it all and it’s hidden from source control. Data access within the application hasn’t changed, and we no longer have to concern ourselves with how sensitive it is.
We can now be sure that giving access to a repository does not imply giving access to other systems.
Epilogue
If you have any feedback on how the blog post can be improved, or if you spot any errors, please let me know by posting a comment below!