A Better Way to Manage the Rails Secret Token
daniel.fone.net.nz
daniel.fone.net.nz
For one example, see this from a year ago: https://github.com/rails/rails/pull/3777#issuecomment-289375...
> If we ignore them, this means a recently created, pushed and then cloned project is not going to work at all.
Some people replace it with a new file upon deployment, some people use ENV vars, some (most) people never open-source their app, and don't mind employees seeing it...
I personally do https://github.com/hotsh/rstat.us/blob/master/config/initial...
Being generic is hard.
> some (most) people never open-source their app, and don't mind employees seeing it...
One of my concerns is that people believe it's only a risk if they ever open source their application. While most apps don't have to worry about a motivated attacker in reality, the risk isn't simply secure or unsecure.
It's more a case of 'more difficult' vs. 'much easier' to compromise. I fear many engineers don't think of securing their apps like this. I know I've only recently begun to understand this way of thinking about security and it's changed the way I code.
To a newbie like me, everything will "just work" in development. In production, I'll get a pretty explicit/precise error message.
Most of my application secrets/configuration/keys/tokens are stored in the database.
The only one that's not is the information about how to connect to the database. That's stored in the DATABASE_URL environment variable and it's stored on each machine. envdir is used to start the apps, reading that environment data.
[1] https://news.ycombinator.com/item?id=5007530
[2] http://blog.phusion.nl/2013/01/04/securing-the-rails-session...
[1]: http://www.gorillatoolkit.org/pkg/sessions#NewCookieStore
Worse. Knowing the secret token allows an attacker to trivially execute code in your application.
Don't ever let your secret token become public knowledge, and if it does, you need to change it straight away.
http://daniel.fone.net.nz/blog/2013/05/20/a-better-way-to-ma...
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails generate session_migration")
# ShopMobile::Application.config.session_store :active_record_store # Your secret key for verifying the integrity of signed cookies.
Does this mean that if you use redis/db-backed sessions you can safely ignore this secret_token parameter completely or even delete this initializer?UPDATE: I just tried to remove it from our environment, and everything seems fine. Unless I'm missing something out, I'd say that's a far better and easier solution overall. It gives you much better control over you session + you don't have to worry about this configuration variable.
cookies.signed['some-id'] = model.id
Rails will use the secret here too.I don't necessarily want that data in my session, or as a user attribute that I have to migrate the DB for. If it's a pkid or something that isn't necessarily sensitive, but I also don't want it to be too easy to get at, I might use a signed cookie.
Now, I've never actually done this... ;p
The primary advantage I intended was that the secret is confined to one part of the infrastructure (e.g. production server) instead of many (e.g. developer workstations, github, etc).
I hate code like this that is explicitly aware of the environment. The code says what it will do in an environment, rather than the environment saying what the code should do in it.
if ENV['SECRET_TOKEN'].blank? raise 'SECRET_TOKEN environment variable is not set!' end
App::Application.config.secret_token = ENV['SECRET_TOKEN']
Even better, raise 'SECRET_TOKEN not set! Please refer to the doc in xyz'
So, the specific method for setting is in an "xyz" doc that your team keeps in a SEPARATE location from the code repo.
And, we really need a standard way to do this, or Github pulls / forks will have more friction or bad security when setting up forks.
Also, I really would rather put it in a file, not system env, as the env might be setup different on different systems, & you'd hate to have that env potentially shared in multi-user systems. Files are more reliably locked down.
In: `actionpack-3.2.13/lib/action_controller/metal/http_authentication.rb`:
raise "You must set config.secret_token in your app's config" if secret.blank?I haven't run into any problems using that method. Does anyone see a reason to prefer environment variables over it?
The envdir program (from daemontools) is useful for setting environment variables in a shell-agnostic way, e.g., run your app with:
envdir ./env python app.py
See alsoUnless you're grabbing that key out of "secure memory", a HSM or a TPM then its not really particularly secure.
Here's a great example of the repo file checkin fail though: http://bit.ly/10dLiDz
The common wisdom seems to be that you can forget about security in a shared environment regardless. The "secret key in environment variable" technique is mostly useful at protecting against malicious employees since it's easy to limit access to the production server but not so easy to limit access to configuration files which are in a Git repository.
Don't trust your environment.
App::Application.config.secret_token = ENV['COOKIE_SECRET'] || '<default secret>'
Secured environments like production get their own secret. Developer machines can use the default w/o additional overhead.$ SECRET_TOKEN=abcdef SOME_OTHER_VAR=hello rails s
or pollute your .profile with a bunch of app-specific variables.
You don't need it to do it; it just makes it easier.
secret_token = File.read('secret_token_dont_check_in')
I fail to see how loading the file into the environment and then into the variable is anything but worse.The entire principle behind HTTP - what enabled it to conquer and dominate the internet, is its statelessness. Storing and trusting things in cookies is a fundamental security design flaw.
Authentication on every request requires you to store the user's authentication details on the client, which is considerably worse for security purposes than a signed or encrypted session cookie.
However, I think that relying on a single easy-to-compromise security token as a single point of failure for your app's security is terrible practice.
A secure user authentication system is usually built by having a users database that contains [user_id, nonce, password_key = pbkdf2(password, nonce)]. When logging the user in, you should store this information in a cookie: [user_id, login_timestamp, authentication_token = hmac-sha256(nonce, login_timestamp, password_key)]. Then, on every subsequent request, you would verify that the authentication_token matches what you'd expect based on the login_timestamp and user_id that the client has provided. (Substitute this for any equivalent-security scheme and crypto primitives.)
Now, if you have such a scheme in place, a secret token would provide no additional security. You really shouldn't be storing session information in cookies, but if you must, you can use a key derived from the nonce to sign the cookie, removing the need for an app-global secret. But really, don't do that, you should use a server side datastore for session information (like shopping carts, etc.).
If you do not have such a scheme in place, you app is much more vulnerable to attack. Something based on one global secret token (which by default is checked in to your source code) opens up many more attack vectors that might compromise the security of your app.