You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I want to make this clear:
> This is **NOT** intended to be a "secure" scheme.
As everything is stored in memory that is our largest security measure
here. What this is intended to do is add a additional layer of
computation in case the data is leaked; and to add a minor obfuscation
against internal employees attempting to view the data in-memory.
This uses the same general token hashing algorithm that Devise uses for
things like password recovery tokens. The Devise scheme is as follows:
+-------------+
| column_name |---------+
+------+------+ | +--------------------+
| | | memory |
| | |--------------------|
+--v--+ | | |
+------------------+ | | +-----+ | | +------------+ |
| Rails secret_key +---> KDF +---> key |---+----->| cached key | |
+------------------+ | | +-----+ | +------------+ |
+-----+ | | |
+---------|----------+
|
+------------------------------+
|
| +-------------------------+
| | database |
| |-------------------------|
+--v---+ | |
+-------+ | | +--------+ | +-----------------+ |
| token +---> HMAC +---> digest |------>| digested record | |
+-------+ | | +--------+ | +-----------------+ |
+------+ | |
+-------------------------+
This implementation differs slightly from the above. Namely, this does
not use the KDF based on the Rails secret token. Instead we use a time
sensitive rotating key generated purely from `SecureRandom`:
+----------------------+
| memory |
|----------------------|
| |
| +--------------+ |
+-----------------------+| rotating key | |
| | +--------------+ |
| | |
+--v---+ | |
+-------+ | | +--------+ | +--------------+ |
| token +---> HMAC +---> digest |------>| token digest | |
+-------+ | | +--------+ | +--------------+ |
+------+ | |
+----------------------+
This does not lessen the security. While the KDF is intentionally slow,
the implementation Devise uses caches the result. This means the
expensive computation is only performed once then stored in-memory.
However, it is always computed the same way using PBKDF2-HMAC-SHA1 based
on the Rails secret key and the salt "Devise COLUMN_NAME"; the security
there is purely in keeping the Rails secret key secret (which is stored
in memory).
In our implementation, the secret is only ever stored in memory and
always generated via `SecureRandom`. Thus there's no way to try to
pre-compute the value without exploiting `SecureRandom`. Additionally,
instead of being completely fixed, we change it at a relatively frequent
interval. Thus if there is a compromise of the data (which is in-memory
so either the system is compromised or there's a remote code execution
vulnerability which means way worse problems) only a subset of the data
from that time window is available. If more data is dumped again later,
the key has changed; _nominally_ increasing computation costs of the
attacker (similar, but not exactly like, a unique salt per token).
Why not just salt+hash each token?
----------------------------------
The main problem with token auth is there's no way to look up the token
to know which salt to use. This means you would essentially need to
run through all of the salts to try to find a match.
If we had thought about this more in the beginning we could have
considered choosing longer tokens and using part of them as the
identifying key. Thus allowing us to look up the specific token and
treating the process just like a regular login.
References
----------
- https://en.wikipedia.org/wiki/HMAC
- https://crypto.stackexchange.com/questions/34864/key-size-for-hmac-sha256/34866#34866
- [Devise "generating" reset token](https://github.com/plataformatec/devise/blob/v4.4.3/lib/devise/models/recoverable.rb#L90)
- [Devise algorithm](https://github.com/plataformatec/devise/blob/v4.4.3/lib/devise/token_generator.rb#L21)
- [Devise config Rails KDF](https://github.com/plataformatec/devise/blob/v4.4.3/lib/devise/rails.rb#L41-L43)
- [Rails KDF](https://github.com/rails/rails/blob/v5.2.0/activesupport/lib/active_support/key_generator.rb#L23)
0 commit comments