How to securely store your passwords in a Rails app
January 14, 2025
Every web application needs access to certain credentials to communicate with downstream services. How should these credentials be stored in a Rails app?
Introduction
All web applications need access to some credentials or passwords. For example, if your application allows users to upload and store files and you are are storing these files on AWS S3, then in order to access the S3 bucket, your application will need access to AWS credentials. It is a general security recommendation that, such credentials should not be committed with the source code into your code repository. The reason is that in case the code ever gets leaked out or shared openly, then the credentials will also get leaked with it. As you know, distributed SCMS like git store the whole history on each developer's computer. So, it means that even if you later remove the credentials from the code, it will remain in history. Hence, if any developer's computer gets compromised ever in future, the credentials can get leaked.
So, the general guideline is to store the credentials outside of the code repository in a separate config file. The application should then read them directly from the config file. Some applications load the config file into the environment when the application starts, and the application then reads them from the environment which is equally secure.
So, the general guideline is to store the credentials outside of the code repository in a separate config file. The application should then read them directly from the config file. Some applications load the config file into the environment when the application starts, and the application then reads them from the environment which is equally secure.
Credentials file
Rails adopts the following solution to this problem. All Rails app have a file config/credentials.yml.enc. This file is encrypted and it stores all the credentials the app needs. This credentials file is checked into the code repository. However, since the file is encrypted, even if it gets leaked, no one can read the contents. In order to read the contents, another file config/master.key is needed. And this config/master.key file is not checked into the code repository.
In this set up, you will have to securely share the master.key file with every developer on the team as well as transfer it over to the servers. However, the good thing is that, you have to do it only once or whenever you decide to change the master.key. New credentials will keep getting added as your app grows and new features are added. At that time, the developers just need to add the credential for the new service to config/credentials.yml.enc. There is no need to share anything with the team or to transfer over the credentials to the server. Its a big time saver!
In this set up, you will have to securely share the master.key file with every developer on the team as well as transfer it over to the servers. However, the good thing is that, you have to do it only once or whenever you decide to change the master.key. New credentials will keep getting added as your app grows and new features are added. At that time, the developers just need to add the credential for the new service to config/credentials.yml.enc. There is no need to share anything with the team or to transfer over the credentials to the server. Its a big time saver!
How to use
Since the credentials file is encrypted, it cannot be opened or edited directly. To view the file's contents, on your terminal, do this:
$ ./bin/rails credentials:show
To open the file for editing:
# EDITOR environment variable should have your preferred editor $ EDITOR=vi ./bin/rails credentials:edit
Note that the credentials file is in YAML format. So, you can store the credentials there in a simple key-value format. For example, the credentials file can look like this unencrypted:
aws_access_key: MYKEY1234 aws_secret_key: mysecreat1234
Then from within the Rails application code, access the credentials like this:
Rails.application.credentials.aws_access_key # returns the value of aws_access_key from the credentials file
You can also store the credentials in a hierarchical format. For example, you can organise your credentials file like this:
aws: access_key: MYKEY1234 secret_key: mysecreat1234
Then you access the credentials like this:
Rails.application.credentials.aws.access_key
Using this trick you can configure some key to be different across environments if needed.
Rails.application.credentials.aws.access_key
Using this trick you can configure some key to be different across environments if needed.
Advanced
If for additional security, you don't want the developers to have access to production credentials, Rails supports environment specific credential files as well. For example:
$ ./bin/rails credentials:edit --environment development
Will let you edit the credential file config/credentials/development.yml.enc and its key will be in config/credentials/development.key. Now only share this development key file with the developers and store the master.key only on the production servers.
References
- Rails Guide on Security
- Run ./bin/rails credentials:help on your terminal to see the full manual
- Another nicely detailed overview of Rails Credentials
- Note that this feature has been available since Rails 5.2 but it the default from Rails 8 onwards