The Rails ecosystem has a systemic issue with storing secrets in source code. This violates the principle of least privilege, and undermines a host of protections built into the framework. In essence, it's poor key management, and creates one of the best attack vectors for defeating otherwise-strong cryptography and authentication measures in place.
The issue is widespread:
-
Rails generates
config/initializers/secret_token.rb
(and does not gitignore it). Therefore, most Rails apps use the same secret token for test mode, development mode and production mode, which anyone who can read the application source code has access to.- Most developers are not aware of the confidentiality of this key, but publicity is rising around this vulnerability. See this post for a recent example.
- Many/most OSS apps have versioned secret tokens are surely being re-deployed with those known-disclosed keys unchanged. For example:
- RubyGems.org (Uses ENV but insecure default.)
- Teambox
- BrowserCMS
- Locomotive CMS
- Tracks
- RailsCollab
- Fulcrum
- OneBody
- Fat Free CRM
- Portland Crime
- OpenCongress
- Snorby
- Citizenry
- Calagator
-
Additionally, authors of RubyGems do not usually address the issue of proper secret management when giving instructions to their users. Even Stripe provides a bad example in their sample Rails payments app. There's nothing library authors can easily point to and say: Use this, and you'll be doing it right. Thus, this class of vulnerability propagates throughout the ecosystem.
Devops typically use one of three systems to secure secrets intended for the production environment:
- Store secrets in config files on production which are symlinked in by Capistrano at deploy time. This is the most common secure approach. 37signals, EY, etc. use this.
- ENV vars. Heroku strongly pushes this. It can also be done outside of Heroku using envdir or rbenv-vars. This can be a bit harder to setup outside of Heroku. For example, I use envdir but it's not trivial to have environment changes apply across graceful Unicorn restarts, so I've switched back to stopping the processes entirely and starting them from scratch.
- Store encrypted secrets in version control (e.g. app version control or an ops repository), and limit the decryption key to production. Twitter uses this system.
- Self-documenting. The presence of the config instantly drives awareness.
- Secure-by-default. We should ship with a secure configuration, requiring explicit developer action to make it insecure.
- Simple to deploy. Translation: don't require envdir or rbenv-vars.
config/database.yml
can contain passwords. In fact, for production environments, it should. How do these relate to any proposed solution?- Secrets are by nature environment-specific. How does this relate to existing Rails configuration infrastructure (specifically,
config/environments/*.rb
)?