New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolve critical vulnerability allowing arbitrary tokens to pass as matching #60
Conversation
|
@justinas I strongly recommend publishing a CVE for this so that people using - for example - nancy have a chance to detect and patch this. |
|
Looks bad. And obvious in hindsight Let me literally sleep on it (getting late here) and perhaps proceed with the CVE and this PR tomorrow. |
|
Awesome, thank you for the quick reply! As far as I can tell, unless you're not using I found this because we're mocking |
token.go
Outdated
| if len(s) == 2*tokenLength { | ||
| s = unmaskToken(s) | ||
| } | ||
| return subtle.ConstantTimeCompare(r, s) == 1 | ||
| return subtle.ConstantTimeCompare(r, s) == 1 && len(r) > 0 && len(s) > 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would a more restrictive check make sense, i.e. len(r) == tokenLength && len(s) == tokenLength? I think we can also move the length checks before slice comparison. ConstantTimeCompare is already non-constant if slices have differing lengths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, it would probably be best to just delegate to verifyToken here after unmasking realToken (verifyToken expects one masked, the other unmasked
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would a more restrictive check make sense, i.e. len(r) == tokenLength && len(s) == tokenLength? I think we can also move the length checks before slice comparison. ConstantTimeCompare is already non-constant if slices have differing lengths.
Will do! I'm busy with workshops until Friday so I won't be able to fix this before but am happy to do so on Friday :)
Also, it would probably be best to just delegate to verifyToken here after unmasking realToken (verifyToken expects one masked, the other unmasked
🍝 ). It already has such checks and there are less opportunities for footguns having one implementation rather than two.
I'm not a 100% sure if I caught that but I have to say that I also don't 100% understand how masking/unmasking works and what they do. If you point me to the right areas I'd be happy to give it my best try but if you already have a concrete idea on how to do it feel free to go ahead :)
Enforced a single point of token equality check for all functions. Added a test case to demonstrate why the verifyToken can not just be used.
|
Thanks to @zepatrik your comments have been addressed :) |
|
Looks good. I will see into getting a CVE assigned. |
|
Thank you :) |
|
|
Before applying the patch to
token.go,VerifyTokenwould incorrectly allow the following pairs to appear as equal:This opens up attack vectors when developers rely on
VerifyTokenin cases where the attacker could trick the first parameter (realToken) into being an arbitrary value. Given thatVerifyTokendoes not rely onr *http.Requestto extract the token, this could happen when misusing the API or when other exploits are found to the waynosurf.Token(r)works.This patch handles empty values and base64 decoding errors as verification failures and allows the failing test cases to pass.