Extracting secret credentials from your code
November 28, 2015
If your source code contains credentials for your production environment, you're looking for trouble (do you remember the Ashley Madison leak?).
Still, I often find Rails apps where
database.yml contains the credentials to access the production database, a hardcoded
SECRET_TOKEN or fragments like:
|if Rails.env.production? SomeService.api_key = "..." elsif Rails.env.staging? SomeService.api_key = "..."|
|SomeService = ENV["SOME_SERVICE_API_KEY"]|
Platforms like Heroku provide advanced and secure ways to configure this environment variables in your servers, but it may sound difficult to implement in development for two reasons: - Development environments tend to store and run different apps, which may cause name conflicts in the env. variables. - Setting up the environment variables manually when a developer joins a team can be time consuming and error-prone.
There are two approaches to overcome those issues. Both require you to write your configuration variables in a text file called
.env. For our previous example the
.env file would contain:
This may sound as dangerous as storing the credentials in the repo, but there's a difference. You'll have a
.env file for each environment (you can call them
.env.test can be stored in the repo (as they only contain credentials for local or safe-for-development services), so all the development team can share them, making very easy to keep them in sync and using them to setup new development machines.
For your servers, you have a two options:
- Setting actual environment variables manually or with the automation system of your choice.
.env.stagingfiles, which will only be stored in their respective servers and will never need to be in your repository.
Now, how can you load the configuration from an
.env file so your app can access it? I'll describe two options.
Loading the credentials from the app
There are libraries like
[dotenv](https://github.com/bkeepers/dotenv) for Ruby that allow us to load the environment variables from an
.env file. Check the README or this tutorial to see how to integrate
dotenv with your Ruby app.
If you don't use Ruby, there are similar libraries for many other languages.
Preloading the credentials before starting the application
Procfile follows a simple format:
process-name: the command to run that process.
Let's experiment with a simple
Procfile defining two processes:
echo Hello, printing the fixed value
echo My name is && echo $NAME, which also reads and prints the value of the
Procfile will look like:
|test1: echo Hello test2: echo My name is && echo $NAME|
Now, let's run those processes. I'll use
foreman in my examples, but
forego and Heroku Local work similarly.
We can run the first process with:
|foreman run test1 # Output: Hello|
and the second with
|foreman run test2 # Output: prints My name is|
Now, if we write a small
.env.development file containing
we can now ask
foreman to read it to configure the environment variables used while running
|foreman run -e .env.development test2 # Output: My name is Raul Murciano|
We can extrapolate this little example to run things like a test suite or an app by using
foreman run -e <SOME-ENV-FILE> <SOME-COMMAND>.
The only drawback I see with these approaches is that both of them a dependency, in the form of an external tool (when using
forego et al) or an internal library (in
dotenv or the different ports to different languages).