Where to store sensitive data in public rails app?

2019-01-10 08:57发布

问题:

My personal rails project uses a few API's for which I store the API keys/secrets in config/environments/production.yml and development.yml as global variables. I now want to push this project to github for others to use, but I don't want them to have those bits of sensitive data. I also don't want this file in .gitignore because it's required for the app to run. I've considered putting them in the DB somewhere, but am hoping to find a better solution.

回答1:

TLDR: Use environment variables!

I think @Bryce's comment offers an answer, which I'll just flush out. It seems one approach Heroku recommends is to use environment variables to store sensitive information (API key strings, database passwords). So survey your code and see in which you have sensitive data. Then create environment variables (in your .bashrc file for example) that store the sensivite data values. For example for your database:

export MYAPP_DEV_DB_DATABASE=myapp_dev
export MYAPP_DEV_DB_USER=username
export MYAPP_DEV_DB_PW=secret

Now, in your local box, you just refer to the environment variables whenever you need the sensitive data. For example in database.yml :

development:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: <%= ENV["MYAPP_DEV_DB_DATABASE"] %>
  pool: 5
  username: <%= ENV["MYAPP_DEV_DB_USER"] %>
  password: <%= ENV["MYAPP_DEV_DB_PW"] %>
  socket: /var/run/mysqld/mysqld.sock

I think database.yml gets parsed just at the app's initialization or restart so this shouldn't impact performance. So this would solve it for your local development and for making your repository public. Stripped of sensitive data, you can now use the same repository for the public as you do privately. It also solves the problem if you are on a VPS. Just ssh to it and set up the environment variables on your production host as you did in your development box.

Meanwhile, if your production setup involves a hands off deployment where you can't ssh to the production server, like Heroku's does, you need to look at how to remotely set up environment variables. For Heroku this is done with heroku config:add. So, per the same article, if you had S3 integrated into your app and you had the sensitive data coming in from the environment variables:

AWS::S3::Base.establish_connection!(
  :access_key_id     => ENV['S3_KEY'],
  :secret_access_key => ENV['S3_SECRET']
)

Just have Heroku create environment variables for it:

heroku config:add S3_KEY=8N022N81 S3_SECRET=9s83159d3+583493190

Another pro of this solution is that it's language neutral, not just Rails. Works for any app since they can all acquire the environment variables.



回答2:

How about this...

Create a new project and check it into GitHub with placeholder values in the production.yml and development.yml files.

Update .gitignore to include production.yml and development.yml.

Replace the placeholder values with your secrets.

Now you can check your code into GitHub without compromising your secrets.

And anyone can clone your repo without any extra steps to create missing files (they'll just replace the placeholder values as you did).

Does that meet your goals?



回答3:

They're probably best put in initializers (config/initializers/api.yaml) though I think what you've got cooked up is fine. Add the actual keys to your .gitignore file and run git rm config/environments/production.yml to remove that sensitive data from your repo. Fair warning, it will remove that file too so back it up first.

Then, just create a config/environments/production.yml.example file next to your actual file with the pertinent details but with the sensitive data left out. When you pull it out to production, just copy the file without the .example and substitute the appropriate data.



回答4:

Use environment variables.

In Ruby, they're accessible like so:

ENV['S3_SECRET']

Two reasons:

  1. The values will not make it into source control.
  2. "sensitive data" aka passwords tend to change on a per-environment basis anyways. e.g. you should be using different S3 credentials for development vs production.

Is this a best practice?
Yes: http://12factor.net/config

How do I use them locally?
foreman and dotenv are both easy. Or, edit your shell.

How do I use them in production?
Largely, it depends. But for Rails, dotenv is an easy win.

What about platform-as-a-service?
Any PaaS should give you a way to set them. Heroku for example: https://devcenter.heroku.com/articles/config-vars

Doesn't this make it more complicated to set up a new developer for the project?
Perhaps, but it's worth it. You can always check a .env.sample file into source control with some example data in it. Add a note about it to your project's readme.



回答5:

Rails 4.1 has now a convention for it. You store this stuff in secrets.yml. So you don't end up with some global ENV calls scattered across Your app.

This yaml file is like database.yml erb parsed, so you are still able to use ENV calls here. In that case you can put it under version control, it would then serve just as a documentation which ENV vars has to be used. But you also can exlcude it from version control and store the actual secrets there. In that case you would put some secrets.yml.default or the like into the public repo for documentation purposes.

development: 
   s3_secret: 'foo'
production: 
   s3_secret: <%= ENV['S3_SECRET']%>

Than you can access this stuff under

Rails.application.secrets.s3_secret

Its discussed in detail at the beginning of this Episode