Don’t commit sensitive data

Although this might seem a common practice, you would be surprised how many times I come across projects that contain passwords and tokens.

When connecting your application tot 3rd party services or even simpler than that, do you commit your production database settings in your repository ass well? Do all team member need access to this production data? What if a team member leaves the company, are you going to reset this data for all your running projects?

Extract sensitive configuration data

When you look at a default Rails application, you will see some out of the box data that could be extracted.
First of all, the secret token. You can find the secret token in config/initializers/secret_token.rb and will look something like this:

MyApp::Application.config.secret_token = '3eb6db5a9026c547c72708438d496d942e976b252138db7e4e0ee5edd7539457d3ed0fa02ee5e7179420ce5290462018591adaf5f42adcf855da04877827def2'

The secret token is used to sign cookies that the application sets. Without the secret token, the cookies that the browser sends wouldn’t be trustworthy. I’m not going into detail on the consequences that this might have, but there was a security issue some time ago with Rails 3.2.10 that allowed remote code execution when someone knew the secret token.

Secondly, you have your database authentication settings. These are stored in config/database.yml and look like:

development:
  adapter: postgresql
  host: localhost
  encoding: unicode
  database: project_development
  pool: 5
  username: developer
  password: devpwd

test:
  adapter: postgresql
  encoding: unicode
  database: project_test
  pool: 5
  username: tester
  password: testpwd

production:
  host: remotehost.com
  adapter: postgresql
  encoding: unicode
  database: project_production
  pool: 5
  username: production_user
  password: prodpwd

Here you see that the production data is present as well.
And then comes all other sensitive production data like oauth tokens from different services.

dotenv

Dotenv is a handy gem that loads environment variables from a .env file into an ENV variable. You can use the gem in combination with Rails ( dotenv-rails ) or in any Ruby codebase ( dotenv ).

You can choose to create a general .env file or one for each environment such as .env.test or .env.development.

Then just fill the file with all sensitive data:

SECRET_KEY='3eb6db5a9026c547c72708438d496d942e976b252138db7e4e0ee5edd7539457d3ed0fa02ee5e7179420ce5290462018591adaf5f42adcf855da04877827def2'
DB_USERNAME='production_user'
DB_PASSWORD='prodpwd'

In this example if fill the SECRET_KEY variable with the secret token. Now just replace your secret token in your application to point to the ENV variable:

For your database credentials, alter the database.yml file to:

...
production:
  host: remotehost.com
  adapter: postgresql
  encoding: unicode
  database: project_production
  pool: 5
  username: <%= ENV['DB_USERNAME'] %>
  password: <%= ENV['DB_PASSWORD'] %>

Yes, you can add ruby to your database.yml file since it gets run through ERB.

So simply put, replace all sensitive data from your application and put them in your .env file. Then just replace it with the correct key in the ENV hash.

Deployment

The dotenv comes with a handy capistrano recipe which will symlink your .env file for every deploy. Open your Capfile and require the dotenv/capistrano file:

Next up, open your config/deploy.rb file and set the path where you will store the .env file. In this example I’m storing it in the shared_path under a config directory

Now you only need to upload your .env file to the correct location. You could do this with a simple capistrano task:

task :copy_configs, roles: :app, except: { no_release: true } do
  run "mkdir -p #{shared_path}/config"
  top.upload("#{Dir.pwd}/.env", "#{shared_path}/config/.env", via: :scp)
end

And call the copy_configs task after your setup:

after 'deploy:setup', 'deploy:copy'

Now your .dotv file will get uploaded through scp ( secure copy ) after you run cap deploy:setup .

Note

  • If you don’t like the ruby code in your database.yml file, you can always ignore the whole file, upload it to the shared config folder and then symlink to it after every deploy.
  • If you already included some sensitive data in your repository, check out the github article on how to remove sensitive data