How to keep your credentials a secret when working with Rails.
When working with public facing code, hiding things like API keys or other credentials is serious stuff. When you push your code to places like GitHub, that code may be visible and accessible for all to see. That would include any credentials that you need for your app to work.
This seems to be an issue that has only gotten worse since GitHub implemented their search function. While this does make code you want to work on easier to find, it also makes any sensitive code easier to find. There are many examples of developers unknowingly pushing code up to GitHub that contains this information. There are also plenty of people out there that know about this and are willing to exploit it.
I recently ran into this problem when trying to add functionality to my Flatiron School final project. The back end is built in Rails and connects to several APIs which all require their own credentials. I got everything working in my local environment but then wasn’t sure how to handle the problem of keeping those credentials a secret.
After a bit of Googling, leveraging some built-in Rails functionality, and some Git-fu, I had my solution. It involves adding credentials to the ‘secrets.yml’ file in the Rails build and then turning those credentials into environment variables that then become available to the rest of the app.
Sounds a bit involved but it really isn’t too bad, I promise. I’ll walk you through creating a Rails app, adding secret key data to it and confirming that it is available to the rest of the application.
First step is to create your Rails app. This is done with the rails new
command followed by the name of your app. I’m calling mine ‘hide_keys’.
rails new hide_keys
This will give you all of the boilerplate rails directories and files.
From there, open your text editor and open the ‘config/secrets.yml’ file. The file will already include your secret_key_base number as well as the ERB (Embedded Ruby, not epic rap battles, sadly) syntax for creating the environment variable.
Include your super secret credentials under the ‘development’ and ‘test’ sections of the file. Also add the environment variable to the ‘production’ section of the file.
At this point, you need to take a bit of a side step and think about this file in terms of Git and GitHub. This file now contains some secret information that you don’t want anyone to get a hold of, so you probably shouldn’t include it in your Git or GitHub repos since those may be public facing.
How do you tell Git to ignore a file? With the .gitignore file of course!
“I Don’t have a .gitignore file!” you say? Oh but you do! Your Rails build comes with one (or you can just create your own in your Git repo). You can read more about this here, but the quick version is to just create a .gitignore file and add ‘secrets.yml’ to it.
With this line added, my git status
results go from this:
To just this:
There is now one more step to start creating your environment variables and this is where a bit of Googling and asking around helped me. When researching this issue, I was directed to this Stack Overflow (SO) post which contained this code block
config_files = ['secrets.yml']
config_files.each do |file_name|
file_path = File.join(Rails.root, 'config', file_name)
config_keys = HashWithIndifferentAccess.new(YAML::load(IO.read(file_path)))[Rails.env]
config_keys.each do |k,v|
ENV[k.upcase] ||= v
end
end
Let’s break this down a bit to see what exactly is going on.
config_files
is a variable that contains an array of file names. In this case, it is just one name but the each loop that follows could be run against a list of files in this array.
The next line is initializing an ‘each’ loop to run against the config_files
variable.
file_path
is another variable that is set to a string. This string is a joining of three separate strings of the Rails root path, the string ‘config’ and then the file_name
which is the name of the file that the each loop is being run against. So after all of this, the file_path
variable is now set to
/Users/jason/Development/scratch/rails_env_variable/hide_keys/config/secrets.yml
Whew. Okay, carrying on…
There is a lot going on in the next line. In starting to research it, I realized it was quickly getting to be out-of-scope for this particular post. Since I don’t want to stray too far off topic here, let’s just say it performs some Ruby magic that turns the keys and values contained in the ‘secrets.yml’ file into a hash.
(I can dive deeper into this in another post and will link to it once it’s up.)
At this point, the config_keys
variable contains this hash {“super_secret_key”=>”123456ABCDEF”}
The next line is another each loop which iterates through the config_keys hash assigning each key/value pair as an environment variable. This essentially adds each key/value to the ENV hash which is the hash of environment variables Rails uses.
So, now that you understand how this script does what it does, what do you do with it? Copy and paste it into your ‘config/application.rb’ file. The code in this file is run whenever the Rails server is started up.
So, finally, when you start your rails server you can test to make sure this worked by going into the Rails console,rails c
, and looking for the environment variables in the ENV hash.
As another test that Rails has access to this information, I pushed this key out to a web page.
Now you can add whatever credential information you want to the secrets.yml file and it will be available to your Rails application but stay hidden from prying eyes.
This is just one example of how this can be done. And you need to know about it before the information becomes compromised. What if you realize too late that you’ve pushed up information that others shouldn’t see? Luckily GitHub provides a guide on what to do in that situation.
Most importantly:
“ Once you have pushed a commit to GitHub, you should consider any data it contains to be compromised.” — GitHub