Secure configuration of Rails applications

It’s good practice to keep these configuration secrets (like API keys and passwords) only on the server and not in version control, especially not the production ones. Here’s a bit more why to separate configuration/credentials and code.
Here’s a story of someone who got a huge invoice because he had sensitive keys in git.
If you need to remove something from the git history, you can still use BFG Repo-Cleaner or git-filter-branch.

Store configuration in the environment

  • You’ll be able to access the environment variables through ENV["KEY"], or <%= ENV["KEY"] %> in Rails’ YAML config files (config/database.yml and others (see below)). If you want to make sure that all keys are actually set before running the application, use ENV.fetch("KEY") instead. It will throw an error so that you’ll find out quickly if the key doesn’t exist.
  • Rails 4.1. added the config/secrets.yml file which makes the variables stored in there accessible through Rails.application.secrets, for example <%= Rails.application.secrets[:database][:host] %>. In config/secrets.yml you can still use the environment variables (and for production you should).
  • Rails 4.2. added config_for. In the configuration you should still use the environment variables <%= ENV.fetch("KEY") %>.

How to manage environment variables

  • Don’t keep the environment variables in the .bashrc or .bash-profile files because that means they’re available to every process that you run as that user. Think about the worst case scenario, a bug in one of those processes would maybe expose those variables. Below you’ll find some other options, preferably choose one that makes the variables only available to the Rails process.
  • Load environment variables from .env files. But don’t commit those files. Their purpose is to configure a development system, but could be used on the server, too. This means you’ll have to set a reminder to change the production environment when deploying a new version. This will only make the env variables available to the Rails process, not as global variables.
  • Rbenv-vars and .ruby-env files in RVM  work similarly without adding another gem dependency.
  • There are deployment tools like Ansible, Puppet or Chef to configure a server environment. However, don’t set global environment variables with this.
  • Heroku or other PaaS services provide command line tools to set the environment variables on the server. Figaro can help simplify that process.
  • However, you’ll still need a process of how new or changed development config variables get to the right place. Some teams use central wikis to keep the newest (development, safe-to-share) environment variables.

So use environment variables to manage all in one place, get it to the developers and production using one of the above solutions and maybe use Rails’ config_for or secrets.yml (with environment variables in it) if you prefer that syntax.

Keep environment variables secure

  • As this piece points out, a process run by your Ruby/Rails app will inherit the environment variables of the parent process. So you should empty out the environment variables before executing a command that you haven’t written.
  • For example with: system(Hash[ENV.map {|key,value| [key, nil]}], "echo $RAILS_ENV") RAILS_ENV will be empty for the echo process.
  • Secure the environment variable files by giving them only the permissions that they really need: chmod 600 .env.