Back to Posts

Credentials in Rails before and after 5.2

Posted in Rails, Security, Technical

When building any web application, you will run into a thousand tiny pieces of sensitive information that you need to keep track of, but that you also can’t commit to source control. This is stuff like passwords to third-party services that your app needs to know and paths to cloud resources. These bits of information go by many with slightly different names: secrets, credentials, configurations, environment variables. There are slight differences in the meanings of these terms, and we’ll discuss some of those differences, but for now you can think of them as the same thing - at least the first three - and you’ll be okay.

Rails has changed the way it handles secrets in 5.2. As some teams migrate their legacy codebases to Rails 5 and beyond while others stick with earlier versions, it becomes important to clarify the difference between how creds are done before 5.2, and how they’re done after. It’s likely that, as Rails devs who work in all sorts of environments, we will end up dealing with both well into the future.

In this post, we’re going to quickly review environment variables as a concept, then discuss how Rails made use of them leading up to 5.2, and how it has since then moved beyond them.

A quick review of environment variables

Linux gives us environment variables, which is a collection of variables that are globally available, but specific to that shell instance, so they need to be set every time you create a new terminal instance; they do not persist from one terminal instance to another. They are used for a million different things: from setting the basic settings of our shell to configurations for the applications that we build ourselves.

In terminal, we can access the environment variables of our shell via the ‘env’, ENV, or ‘printenv’ command.

In our Rails code, the terminal’s environment variables can be referenced via the ENV variable. Unlike the env variable in the terminal, the one in Rails is case sensitive - it must be upper case.

Rails secrets pre-5.2

Pre-5.2, secrets work like this:

We have a config/secrets.yml file. Technically, you could put all of your secrets directly here. But by convention, this file was used more as a liason between your rails code and the linux runtime environment that your app was running in, and which contained your secrets, stored in environment variables. In other words, this file does not hold the actual values of your secrets/environment variables; it only holds the names of those secrets, and retrieves the actual values from ENV. This file gets committed to version control and is visible to anyone with access to the codebase. It allows for the application to access secrets through the Rails.application.secrets interface, instead of having to directly access the ENV from a thousand different places throughout the app. This approach also has the advantage of neatly documenting all of your secrets/configurations without exposing their values.

The actual “secret values” themselves only live in your local ENV, or, if you are using the dotenv gem, they live in your .env file, from where they are loaded into your ENV, and from where they are picked up by the config/secrets.yml file.

In production, the ENV is populated by some method or another - it’s usually specific to which platform or cloud provider you are using. (Dotenv is only really meant for use in development.) But either way, once the ENV is populated, the propagation of the secrets through the system works pretty much identically as in development.

Rails secrets in Rails >= 5.2

Starting in Rails 5.2, secrets work like this:

The whole system for how credentials are managed is different. The dependency on linux environment variables is gone. No longer will devs have to set a bunch of variables in their environment. Rather, the way things work now is that the creds are written out in a file (that will not be committed to source control) as part of the Rails app, and that file is then encrypted with a master key. The encrypted file is committed to source control, but the master key is not, and you need the key to be able to read or edit the credentials. So the credentials, through the magic of encryption, are up there on Github for anyone to see, but not readable or editable. This makes dealing with secrets between environments much easier, because instead of having to deal with a bunch of secrets, you just have to deal with one master key, stored in config/master.key.

David Kennell is a web developer, specializing in Ruby on Rails, Javascript, and React. His most recent tech-related interests are DNS and database normalization. In his free time he enjoys not making up a list of hobbies for his blog bio. :D

Read Next

Know your Numeronyms in Tech