Proposal: Dynamic secret_tokens

Hi all,

Security is always a hot topic, and in our company especially.

We where looking into the secret tokens. And we think we can do a step better than an “secrets.yml” file.

The fact is that system administrators still have access to the secret token, and that is not always acceptable.

Replacing the secret token each time an admin leaves, is not a viable solution. So we fought, how about a dynamic token?

Proposing to make the token “callable”. Besides being a string, the token could be a proc or anything responding to call, receiving the request object.

This allows the implementer to dynamically change the token.

This can be useful to have a separate token per domain, very useful in multi tenant applications.

If there is intrest in this, I’m willing to develop it as well!

What do you think?

This sounds great and would also helpful in updating Rails (I know updating is a breeze for some apps, but not always).

If you wanted to attempt support for a dynamic secret, you could give it a go, but I feel I should correct one thing.

Systems administrators will always be able to read your secret, there is no avoiding this, they can fire up rails console and run:

irb(main):002:0> Rails.application.config.secret_key_base

=> “f1a4df910e88f985dd555dd4ad0210b4cc8e9ca8306ab8ae29164d4c8874b5yesthisisrandomnoisenotmysecret6ad01ac7762ede919f5f5c513bf866654d1355”

Even if we tried to put in mitigations for this risk, there’s always GDB. So if you want dynamic secrets for some reason, give it a go and see how the patch turns out, but don’t do it to make your secret ‘admin proof’, that’s not going to happen.

I think Michael makes a very good point. We trust our admins with root access, so I can’t imagine why we’d go extra mile hiding an app secrets from them when they are so powerful with root already. If I had a sysadmin that I couldn’t trust handling this token properly, I would never hire him in the first place. As long as the secret accessibility is restricted to an appointed unix group (e.g. “deploy”) - that should work, shouldn’t it?

Not exactly if you use environment variables with secret settings using deployment tools like Capistrano.

But I also don’t see why a system admin shouldn’t be trusted…

Forget about using environment variables. Those are the easiest to checkout if you're root in a Linux server for instance. I could easily read it in a quick test.

I'm curious though to see how easy would it be to use GDB to attach to a running application after the deploy script has removed the file with the key after the application has booted...

Trivial. If it's in memory, you can rip it out.


That specific approach wouldn’t really work in this case as you’d have to restart the process with the shim in place but you wouldn’t be able to because you’d be missing one of Rails required initializer, which contains the secret.

Maybe the admin would be able to install the shim globally, but if you don’t really trust your admin you could create some kind of checksum of your system of and refuse to deploy if it has changed. Softwares like Bacula are able to do things like that. Then you could diff with your known state to see what has changed so that you’d know whether the changes are safe or not. Bacula has such a feature for instance.

But since this requires a lot of work, it would be much simpler to not keep employees you don’t trust. There’s much worse things system admins could do if they want to.

In any case I don’t find it trivial to steal the secret from a running process.

Valid point. However, security is never a 100%.

I do think that N secret tokens is a "safer" situation than just one.
Also note, that any future tenants would be safe from "remembering" the base token.

I'll give this a shot this weekend.

I would rather have the system accept multiple tokens at one time so you can rotate them on a production server. This is easily something you could set up via cron and if an attacker gets a token it would only be valid for hours instead of days/months.

Heroku would love to rotate your tokens for you but right now we can’t. When you serve a page and then change a token then any forms that page submits will be invalid to the next server with the new token.

I’ve been looking into this and it is possible, however I don’t think it is needed.
I’m going to describe how it’s possible in rails 3 and 4; and why I don’t think it’s needed in rails 4.

Rails 3:

In rails 3 action_dispatch uses the secret_token that is added to the request env. This is then used by other parts of the application. If you want to make this dynamic it’s as easy as changing the value of “action_dispatch.secret_token” key in the request env early in the middleware stack.

Rails 4:

In Rails 4 there is quite a bit more going on. Action_dispatch is now using: secret_token, secret_key_base, key_generator, http_auth_salt, signed_cookie_salt, encrypted_cookie_salt, and encrypted_signed_cookie_salt. Just like in rails 3 these values are fixed for the entire application. The “bad” thing here is that the key_generator is initialised once with either the secret_token or secret_key_base. Even if we change these values later with middleware the generator will keep using the values it was initialised with.

It’s perfectly possible to swap out the generator on a per request basis, just like swapping the secret_token in rails 3. I have no idea what the impact of that would be, but I think it would be minimal. An alternative would be to change the salts per request. I think this could even be better than switching out the generator entirely; and can be easily achieved with some middleware.

I hope this info is useful for others!

Maybe we can explain this in the action_dispatch docs?

I’m neutral on the feature of dynamic secret tokens, but I’ll weigh in on your implementation choice. Please don’t use the callable approach for it. That’s appropriate in a more functional language, but in Ruby, we’d like things to be objects and use semantically meaningful message names. The problem with passing around procs is that you can’t tell anything about them - they are just procs, and calling them looks like like calling any other proc. A simple duck-type for secret tokens will give you more flexibility and make it easier for others to understand and work with.