-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Use faster OpenSSL secure comparison if available #1711
Conversation
cb870a5
to
2b3a4d2
Compare
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.
I'm fine using the faster implementation in the openssl extension. Only one minor implementation comment.
2b3a4d2
to
391ae79
Compare
Comparing a 16 byte string: openssl_secure_compare: 9397508.4 i/s rack_secure_compare: 515938.0 i/s - 18.21x (± 0.00) slower
391ae79
to
45ca84e
Compare
Using regular string comparison when comparing signature has different performance depending on how much of the string matched, which can make derivation_endpoint susceptible to timing attacks. We avoid that by using `Rack::Utils.secure_compare` instead. Big thanks to @esparta for reporting this.
Thanks for this! |
|
||
l = a.unpack("C*") | ||
OpenSSL.fixed_length_secure_compare(a, b) | ||
end |
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.
In the current case (as in the case before this PR), there is timing attack that could allow an attacker to determine the number of bytes we are comparing against. However, assuming that that number is not small (16 bytes or more), it seems unlikely that the code is actually vulnerable. Making this raise instead of returning false for a mismatched size would break backwards compatibility.
Sorry, this doesn't sound convincing. Backward compatibility can be achieved by using slower variable length algorithm, then writing a new fixed-length method. |
I fail to see how this PR made things less secure, considering the You are correct that we could add a new fixed-length method that raises instead of returning false. However, I recommend that new code that wanted those semantics call |
Is raising an exception really not backwards compatible? The method documentation clearly says users should not pass strings of differing length. It would only be backwards incompatible in the case that someone is using the method wrong. If I was using this method wrong I would like to know about it, and "returning false" isn't going to tell me that. |
Yes, it really is not backwards compatible to switch from returning false to raising an exception.
The method was not originally documented that way (9a81b96), that was added later (9b4d955). Granted, the warning was added about 8 months after it was added, and that was about a decade ago. I'm not opposed to changing this to raise an exception if we follow our standard deprecation procedure (deprecation warning in Rack 3.1, removal in 3.2 or 4.0). |
If one uses "#secure_compare" but it is in fact insecure for their use case, wouldn't that justify such a risk? To me it does. |
If this actually opened a timing bug that would allow determination of the compared data, and not just the length of the compared data, I would agree with you. However, as I already stated, if the compared data is of sufficient length, leaking the length is not a large decrease in security. Even if you are only comparing decimal input ( |
This is a assuming a use case, "sufficient length" and non-prior knowledge of a potential attacker. I maintain that if a method promised certain guarantees, it has to provide them or fail loudly. |
If what you are comparing against is not of sufficient length, then it is going to be faster to run a brute force attack on progressively increasing lengths, than a timing attack to determine the exact length followed by a brute force attack on the exact length. If the attacker has prior knowledge of what the comparison values might be for length The method already documents that it leaks length info via timing attacks, so it already provides what it promises. |
Benchmark