Cleartext Storage of refresh tokens
It seems to me that clear text storage of refresh tokens (and other credentials in general) isn't the greatest idea. Refresh tokens specifically seem to provide any attacker who compromises the database with long lived credentials, which would be akin to leaving the resource owners password in cleartext.
In other words, if I have resource owner passwords hashed with something like bcrypt, and then implement refresh_tokens, i've basically negated much of the security of bcrypting the resource owners passwords.
RFC 6819, "OAuth 2.0 Threat Model and Security Considerations" does specify in section 18.104.22.168.3 that there should be no cleartext storage of credentials.
- 点赞 评论 复制链接分享
- weixin_39978350 4月前
Are there any updates on this? I see access tokens as passwords, and in that case it is just wrong to store them in cleartext.点赞 评论 复制链接分享
Are there any updates on this? I see access tokens as passwords, and in that case it is just wrong to store them in cleartext.
Agreed. This conversation is related: https://github.com/doorkeeper-gem/doorkeeper/issues/675
Encrypt them is an alternative, what I'm really interested in doing is not storing them altogether.
I won't have time for a few weeks to work on this though.点赞 评论 复制链接分享
- weixin_39639260 4月前
Now we have optional hashing for token & client credentials and secrets :fire: #1168
It will be released with the next version of Doorkeeper点赞 评论 复制链接分享
:+1: to the roadmap or PR. Thanks for your feedback.点赞 评论 复制链接分享
Great... I might look into a PR if I end up with some free time. Has there been any consensus on an encryption protocol here. I would assume that the client applications will not need to encrypt or decrypt anything as tokens are assumed protected by SSL in transit and it's the clients responsibility to protect tokens in memory. If that's the case, we could simply use an asymmetric protocol with a secret held by the authorization server. (ie simple AES cipher versus RSA public/private key)点赞 评论 复制链接分享
If you were to embark on using crypto then use the right tools for the job, use symmetric crypto as opposed to asymmetric crypto like RSA. Ensure tokens are authenticated (ie with a MAC) and encrypted. I posted some example code for token authentication+encryption over on a devise related gist here: https://gist.github.com/josevalim/fb706b1e933ef01e4fb6#comment-946246.
But before beginning the quest, it is worth considering that there is little you can really do in your app software to protect an intruder that is already inside the fortress in the attack scenario above, eg a server compromise allowing access to the information stored in the database. Not only can the attacker get access to tokens they can get all the information you are trying to protect too. Any secret you use to encrypt/decrypt tokens will be sitting there too in a config file and trivially accessible by the same intruder.
Lets take it further, what if you use a smartcard or HSM (hardware security module) on your server to encrypt things?, where then will you keep the secret to authenticate your application to the HSM? Perhaps you require manual key entry every time you start each and every application/service on the server? How will you ensure availability with such inconvenience to operations? What will you do to ensure your server software hasn't been trojaned to reveal keystrokes, tokens or the token encryption secret or the very information you are trying to protect through some back door? The scenario above has an intruder getting their hands on the database, and while they are there they can hide their tracks and possibly install other nefarious software, modify your rails app etc...
Encryption often sounds like a magic bullet, but there are many scenarios where it doesn't really achieve anything real without additional controls, almost anything you do to mitigate the above attack scenario using encryption really amounts to obfuscation, but more importantly will mislead people into thinking its more secure than it actually is. Its not a case of layered security if its all just tissue paper.
There is however real value however ensuring unauthorised access and changes are mitigated and that any unauthorised changes to your system are detected. You need make sure there is no unauthorised physical access to the server too as there are many ways to capture the contents of memory exploiting USB, capture/intercept network/storage traffic, install root kits etc点赞 评论 复制链接分享
I agree that an attacker with full access to all of your machines has basically owned you, and encryption will not help. However, that's not a reason to prevent leaving tokens in cleartext in storage. Often times database machines get compromised on their own. If the encryption key is on a separate application server, that machine needs to be compromised as well. Even more likely, and the most frequent form of compromise, is something like a SQL injection attack. In that case although an attacker might be able to read or even insert data into the storage system, they should still be prevented from impersonating and authenticating as a user of the application. A dump of a compromised database to pastebin (which is all too common) wont allow people to start authenticating as users. Another example which may seem trivial, but has real world consequences in some organizations, are employees with read access to storage systems who, again, should not be allowed to impersonate/authenticate as users in the application.
Can you explain why you think asymmetric crypto is needed in this case? Distributing public keys to clients is a lot more of a hassle than simply having a single key on the application server to encrypt/decrypt. I'm assuming that the client and server are communicating over SSL (which includes a MAC), hence the token can be sent cleartext over the wire to the client.点赞 评论 复制链接分享
I said use symmetric crypto as opposed to asymmetric crypto like RSA. Asymmetric crypto is definitely not the right tool for this and my link points to some example symmetric encryption code that could be adapted for doorkeeper. The reason you want authenticated encryption (ie generating a MAC) for your tokens is to adequately protect data at rest from accidental or unauthorised modification. It will better protect against SQL injection since without the secret key you can't inject a valid token in the database.
I am not opposed to implementing encryption and most of my career has been engineering transaction security and identity management products as well as developing information security standards, frameworks and conducting TRA's. Hence why I am saying it is important to think about what it is you are protecting against vs what is NOT protected against and be very clear in the documentation on the real protection given for typical configurations/deployments because almost everyone does security badly. I'm not too interested in the shiny encryption of tokens for the particular attack scenarios given, I'm more interested in the gaping hole that leaves the high value information (eg user enrolment information, and personal and financial information) unprotected in your attack scenarios. Your SQL injection scenario is in that aspect is a little contrived since if the attacker can mount such an attack they don't need the tokens and can instead be off with the valuable information your access tokens are supposed to be protecting, little has been done to protect the real gold.
Put your attack scenarios in context and understand what your threat agent is trying to do and where the payoff is. Acquiring tokens are a means to an end, the end being acquisition, modification or destruction of the information your application manages or injection and distribution of malware to end user systems for end target(s) unrelated to your system. Placing value on the assets (ie asset classification) is important, tokens are ephemeral and can be deleted after an attack whereas your user enrolment information and the other information your application manages remains valuable long after the attack and can't be repaired or verified without other controls such as backups and audit logs.
My point is if you care about tokens being encrypted at rest, then you should care MORE about all your real data being encrypted and authenticated at rest AND your system and environment being controlled by actually implementing the ISO 27000 Information Security standards. Anything less is really just hand waving.点赞 评论 复制链接分享
Sorry... misread your initial post. I would agree with you, I think symmetric crypto is the way to go.
I'm not sure what you're arguing though. Even if we should care "MORE" about real data being encrypted, there are still valid reasons to prevent oauth tokens from being stored in clear text.
The application I'm working on at the moment, for instance, is a system in which unauthorized access to the system is more damaging than access to the data. In other words, if the entire DB is posted to the internet in a public forum, little damage is caused as long as an unauthorized user is unable to log into the application. Specifically the application communicates with third party services, where real damages in the orders of thousands or millions of dollars could be inflicted. Even though "tokens are ephemeral and can be deleted after an attack" the damage that an unauthorized access to the system could cause would already have taken place.
I don't see a reason why the tokens should not be encrypted at the moment. Arguments to the effect that web applications have large attack surfaces and other areas of the application need to be protected don't negate the fact that there is value in encrypting these tokens as well.点赞 评论 复制链接分享
All good. To re-iterate I am not against using encryption but I think the attack surface needs to be considered more fully. For example I believe authentication of ALL authorisation information should be considered, just encrypting the token is not enough, and just encrypting and authenticating the token data at rest is not enough. It would be prudent to authenticate all columns in the oauth_applications, oauth_access_grants and access_token tables with a MAC to prevent unauthorised modification, ie escalating scopes, changing redirect uri, changing timestamps to far in the future, changing the client id or secret and so on.
I think the design of doorkeeper could actually do away with storing tokens altogether. If you encrypt and MAC the authorisation information and put it into the token you can decrypt and verify the token data from the client without having to lookup the authorisation data in the database. You would need a single oauth_applications table so that users can track and revoke applications, and a single revocation table for tracking revoked tokens that haven't reached their expiry. You would still want to authenticate the data in these tables to detect unauthorised changes and at least perform a salted hash of the client secret in the oath_applications table.点赞 评论 复制链接分享
- weixin_39622217 4月前
Hi guys. Any updates on this ? i.e. any plans of adding encryption of credentials/tokens to doorkeeper soon ?
PS :- I can see bcrypt-ruby in the gemspec as a development dependency which gives me some hope that encryption is coming ?点赞 评论 复制链接分享
Unfortunately, the tokens cannot simply be hashed (ie bcrypt). The token needs to be used for the lookup in the access_tokens table, hence it will need to be unencrypted.点赞 评论 复制链接分享
doesn't your argument imply that user passwords can't be encrypted either?点赞 评论 复制链接分享
not exactly sure what you're asking. Encryption generally refers to a reversible process (symmetric or asymmetric), and yes you could encrypt passwords, though hashing is generally more secure since it's irreversible.
So I think you asking why the tokens can't be hashed since we hash password. Hashes need to be salted to prevent simple brute force. With usernames and password you can lookup the salt via the username, and then hash the supplied password to compare it to the database value.
Here we only have a single token, and we can't know how to generate the hash to use for the lookup. Passing back the hash or info on how to hash back to the client would essentially negate the hashing in the first place, you would have basically replaced the plain text token with a hash that's now the "plain text".点赞 评论 复制链接分享
- weixin_39851408 4月前
could you please elaborate why salting is required? As I understand it, salting prevents utilization of rainbow tables to restore the original password. This is important to protect users' passwords when the DB is compromised. As we are dealing with tokens, there is nothing to protect using salts. The time required to brute-force the password/token would be the same - regardless if a salt is used or not.
Wouldn't the following solution work? 1. The request that generates a token returns its plain version to the requester, but stores the hashed version in the DB. 2. The token sent in subsequent requests is hashed using the same technique and compared to the one in the DB. 3. Whenever a specific token is requested, a new token will be generated (see 1.) instead of returning the old token, which would not be feasible because it is hashed.
In terms of the hashing algorithm, I would prefer a quick one. Using bcrypt for user passwords makes sense because they are only checked once and subsequent authorization is done using the session token. Introducing a deliberate delay here makes sense to significantly slow down brute-force attacks. In contrast, the token needs to be checked with every request, so a delay here would result in a penalty for every request, which should be prevented.点赞 评论 复制链接分享
Good point... looking at the code, it looks like the tokens are generated with SecureRandom, so I agree then that adding salt is unnecessary. I also agree that a fast hash would be preferred, brute force of the tokens within the lifetime of the token should be infeasible.
Well that will make things simpler! :smiley:点赞 评论 复制链接分享
As a bystander I think some of the terminology being used here is a little bit off and there is the possibility the least ideal cryptographic tools may be considered as a result.
Tokens represent access grants and are for all intents temporary secrets. They are generated by the server and communicated to the client for later presentation in order to make access control decisions. In the case of doorkeeper those tokens and the associated access control information are currently stored in the database with no cryptographic integrity or confidentiality controls. The most important thing to guarantee here is integrity of the access control information, since without integrity controls an attacker who has access to the database can create whatever grants or access tokens they desire. The next objective is to maintain effective confidentiality of those tokens.
The token when shared with the client should be protected on the wire (typically with HTTPS which provides both integrity and confidentiality). The client is then responsible for managing the security of tokens it receives from servers on its end.
On the server side in order to provide cryptographic integrity to the access grant (what the token actually represents) there is really only one viable option and that is to generate an authentication code which binds both the access grant information AND the token together using a secret key. This means access grants and the tokens representing them cannot be separated and nor can they be forged without the cryptographic key used to generate the authentication code. Such authentication codes are termed a MAC and are not just a "hash" as is being discussed. MAC's are as fast as a symmetric block cipher, meaning they are as fast as it gets in cycles per byte and as strong as the underlying block cipher (which is better than the one-way hashing algorithms we have). In this model the server is both the producer and consumer of the token so independent verification as provided by regular one-way hash functions is not a requirement and not a good choice as anyone who knows (or forges) the token can generate a hash, whilst a secret key based MAC is precisely what is needed since only the server can generate it and they are fast.
It is possible to do away with storing access grants and tokens altogether, as the server can generate a small grant payload in memory, encrypt and MAC it with a secret only the server knows and hand it back to the client as the "token" and then just forget about it. On receipt of a token from the client, the server uses the fast cipher mode to both authenticate the token and recover the access grant information, from which it can then make an access decision (no db access is required to authenticate the token so its very resistant to any kind of authentication based DOS attack, HTTPS will hurt you first). It is very similar to the way rails 4 can sign and encrypt session cookies to avoid maintaining session data on the server but in this case with different algorithms.
To support revocation of tokens the server can maintain a grants table, storing just the MAC of tokens, a revocation status and an expiry date. If one of those tokens ever gets presented the server can simply lookup the MAC and reject it based on revocation status. A periodic job can vacuum the grants table to keep pruning outside the fast path. You can also use a more lightweight variant of this and just maintain revoked tokens in the table if the server is not required to maintain some knowledge of all currently issued tokens.
Not storing the token on the server means the server has absolutely minimal access grant information that can be compromised, forged or abused. Direct access to the database by an attacker can only remove revocation data, and cannot create grants.
I've previously linked to some proof of concept code above on how to use AES-256-GCM for creating tokens so it may assist if this kind of approach is taken.
As I said, I am a bystander here who was interested in doorkeeper as a possible solution at some point in the past however I have since developed my own solution that is simpler, more secure and a better fit to my applications. I am just sharing an approach that has proven to work very well.点赞 评论 复制链接分享
- weixin_40006763 4月前
What was the actual outcome of this thread? I agree that it would be nice to encrypt the tokens, but I'm not sure we've agreed on an approach.
I like 's solution. Are we all cool with this? Does anyone see any issues with this approach? I have the bandwidth to take a crack at this. It would cover any use case where a new token would be handed out (initial creation, refresh flow, etc.)
From the coding side, we'll have to make sure to:
- Delete the old token when we hand out a new one
- ??? Anything else
Would we also want to do this for the application client/secret? This means we would only ever be able to render the client/secret once to the admin creating the application. The admin would have to make a new application if the client/secret was lost (although, maybe that's a good thing). We would have to be really clear for new people using doorkeeper that they won't be able to view it again.
this seems like a thing that would be great for the 2.0.0 release. Overall thoughts?点赞 评论 复制链接分享
- weixin_39975122 4月前
+1 to what proposed.点赞 评论 复制链接分享