Why should subscribers return 2xx on invalid signatures?
aaronpk opened this issue ยท 12 comments
The Signature Validation section says
If the signature does not match, subscribers must still return a 2xx success response to acknowledge receipt, but locally ignore the message as invalid.
What is the reason for returning 2xx here as opposed to a 4xx code?
If we assume that properly configured hubs will always send a valid signature, then the only requests that will hit the callback URL with an invalid signature would be bad requests. Either an attacker or some other misconfigured hub. It would seem that 400 Bad Request
would be a more appropriate response here.
That adds a requirement that the signature be validated immediately instead of queued at all. I could also see arguments for doing so would be leaking results of attempts to guess secret and would make the secret brute forcable.
This should at least be explained/motivated, because at a first look, it seems like it should be a MAY, so sites are allowed to give a 4xx if they want to be helpful.
@julien51 is there a reason this needs to be a MUST?
I'm hoping @julien51 can shed some light on the motivations for making this a MUST. Then we can add a sentence describing that to the spec so this question doesn't come up again later.
Well the reason was a security one. In a world where someone would try to "impersonate" the hub, if we give a signal that the notification was not accepted, we may provide valuable information to the attacker... which may then retry using other signatures. Not providing any info about this is leaving the attacker in the dark.
Now I understand this is very theoretical (like most security issues?) and I can see how it can be helpful for debugging purposes to not have the subscriber return 200.
If you feel like this can be loosen, then I am fine ;)
If the only reason the spec recommends 2xx there is a security reason, and is not for interoperability reasons, then I would actually prefer to loosen the requirement and change it to subscribers MAY return 2xx. That allows paranoid subscribers to still return 2xx, but others can return 4xx if they want.
I am fine with this... but I think we should also mention then that providing too much information to an attacker can represent a security risk. I can see how someone who wants to be helpful include a body with "wrong signature". But again this is not a problem if the secret is sufficiently hard to guess.
In the PR above, I changed:
If the signature does not match, subscribers MUST still return a 2xx success response to acknowledge receipt, but locally ignore the message as invalid.
to
If the signature does not match, subscribers MUST locally ignore the message as invalid. Subscribers MAY still acknowledge this request with a 2xx response code in order to be able to process the message asynchronously and/or prevent brute-force attempts of the signature.
I believe this captures the discussion in this thread. This loosens the requirement on what the subscriber can respond with (this does not have any effect on the interoperability of the spec), and also provides guidance on when you might want to return 2xx.
This has been merged. I will leave this open until we follow up on next week's call.
Thinking about whether this change is a security problem.
The threat is someone impersonating the hub and getting the subscriber to accept a fake notification as if it were real. In some applications, this could be quite damaging.
The attack vector that this change opens up is that an attacker could try to guess the hub.secret value, in a brute force attack, trying each possible hub.secret until it gets a 2xx response. In some systems this attack vector may have been present before, as there may have been other ways to observe if the notification was accepted. In some systems, this attack vector will not be opened by this change, because they MAY still send 200 OK.
The spec does not seem to say how many bits are present in the key, or how they are generated, except to say that it must be less than 200 bytes. (Sorry if I'm missing something.) Searching a bit, I find https://security.stackexchange.com/questions/95972/what-are-requirements-for-hmac-secret-key saying if you're using SHA-256, the secret should be 512 bits.
Brute forcing 512 bits is not possible in the universe as we understand it. Even 128 bits can't be brute-forced, even when you have local access allowing billions of tries per second (which is not what we're talking about here).
I wonder if we should provide any helpful guidance about the hub.secret, like don't hardcode it into your github repo. If people do that, or use something like the current time, it might be guessable. Of course a seasoned professional would never do that, but the WebSub design aims to make the bar for implementing a subscriber quite low. Maybe in 8.2 add a phrase about the hub.secret, that it should be cryptographically random and 128 to 512 bits.
Also, RFC-6151 isn't the right reference for HMAC. It's an informative note about HMAC security; the right reference appears to be RFC-6234, I think.
Does the test suite at least make sure successive subscriptions don't use the same key?
I don't have a test for checking whether subscribers reuse secrets. It's a "should" in the spec, and there's a checkbox in the implementation report, but there isn't a test for it.
I could see adding a suggestion that the secret be cryptographically random. @julien51 any thoughts on that?
I think it should be a should, because it would not break interrop to re-use a secret. But I also agree that one would b foolish to us the same secret over multiple subscriptions... I am fin with adding a suggestion to make it cryptographically random.