Beware of playback attacks, that is, an attacker sniffing the request can reused it as it is even if he does not know the shared secret.
To mitigate the playback attack it is possible to use a challenge-request protocol where the server sends to the client a large random number that the client needs to add to the HMAC input (and that invalidates after receiving the request so that no further requests can be made using the same random number).
I think that actually some kind of get-request-seed request for each request is needed before every call for maximum security. The server will create an entry in the database with an expire set to a given time (for instance 5 minutes, so that flooding the server with get-request-seed will not fill it).
When the request "seed" is used, it gets removed from the list of active seeds, so it can not be used again.
There is still a man in the middle attack:
1) The attacker intercepts our request, send it to the server, and reply to the real client with an error code.
2) This will force the real client to send the request again, this time the attacker will not interfere with the request.
The result is that the request was performed two times instead of one.
After some consideration I would probably err on the side of caution and either use something like IPSEC AH (Authenticated Header), or build a session key exchange protocol using something akin to it's two phase approach, or even just perform HTTPS based request to exchange session keys.
This session key could then be incorporated into each of the HMAC signatures and would be refreshed on a regular basis similar to how IPSEC does it.
I have used AH in the passed for log data which required the receiver to verify the source for auditing purposes, but this was quite some time ago, these days most systems just either use SSL or IPSEC with ESP.
Reinventing the wheel is probably not the best idea in this case.
Will add a note on this to my blog post for completeness.
nonce is ok as long as for your application it is ok to expose the client to playback attack only for a limited time. I would go for a pre-request protocol where you perform a request to get a request-seed, and then use it in the next request (and the server will remove it when already used).
Another thing that makes sense is to use a nonce plus adding the IP address in the string to sign, with server side verification that the IP matches. So at least the playback attack needs to be performed in near time with the same IP as the originating client, that is considerably harder for the attacker to do.
A nonce when implemented correctly doesn't expose the client to replay attacks at all unless I'm missing something here. When requests come in, store the nonce + timestamp and ensure that requests with the same nonce + timestamp are only ever executed once.
"Cryptography is hard, a single character difference will result in a completely different value."
HMAC is designed to guarantee the integrity of the message. If a single character difference didn't result in a completely different value, the whole method would be completely flawed.
"snake case your header names (yes this is bad form) before presenting them to your code as the list of header names."
From RFC 2616, "Hypertext Transfer Protocol -- HTTP/1.1", §4.2, "Message Headers":
Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive.
Yes what I should have said was repeatable signatures are hard across large strings.
Having just worked on this with a few people implementing independent implementations everyone got at least one character wrong, and therefore a complete missmatch of signature.
Right, it's not really a disadvantage. But I wouldn't say that it's a requirement, either. A hash where each input character only affects certain output characters is less than ideal, but it's not a critical flaw. You could make one at least as good as md5 if you had the desire to.
@Dylan16807 Maybe not a requirement, but this could cause information leakage in some scenarios. I'm also pretty confident I couldn't come up with a hashing scheme as good as MD5. I'll leave that to the crypto pros.
Well it depends on what your criteria are for 'better'. If you have three bcrypts glued together that each get character%3, there are no blatant flaws, it's just going to give you an oversized hash for the security level you get. And each character will affect only one third of the hash.
How does one implement a browser single-app JS client (Backbone, Angular etc) to access server API with HMAC authentication? The secret key will be exposed clearly in the JavaScript client.
Unless you can use per-user keys (for example, if you control the server API) you basically can't do it. This is the same problem that DRM faces: you can't give the user the keys to the car and prevent them from driving it.
Comments
Beware of playback attacks, that is, an attacker sniffing the request can reused it as it is even if he does not know the shared secret.
To mitigate the playback attack it is possible to use a challenge-request protocol where the server sends to the client a large random number that the client needs to add to the HMAC input (and that invalidates after receiving the request so that no further requests can be made using the same random number).
I am interested as to how this would be done with a REST API.
Would it be required that a client perform a pre call for each request, or for a window of requests possibly.
I have seen this sort of thing in SOAP aka WS-Security with their SecureToken.
I mite try put something together an example and see how it works out.
Thanks for the comment.
I think that actually some kind of get-request-seed request for each request is needed before every call for maximum security. The server will create an entry in the database with an expire set to a given time (for instance 5 minutes, so that flooding the server with get-request-seed will not fill it).
When the request "seed" is used, it gets removed from the list of active seeds, so it can not be used again.
There is still a man in the middle attack:
1) The attacker intercepts our request, send it to the server, and reply to the real client with an error code. 2) This will force the real client to send the request again, this time the attacker will not interfere with the request.
The result is that the request was performed two times instead of one.
After some consideration I would probably err on the side of caution and either use something like IPSEC AH (Authenticated Header), or build a session key exchange protocol using something akin to it's two phase approach, or even just perform HTTPS based request to exchange session keys.
This session key could then be incorporated into each of the HMAC signatures and would be refreshed on a regular basis similar to how IPSEC does it.
I have used AH in the passed for log data which required the receiver to verify the source for auditing purposes, but this was quite some time ago, these days most systems just either use SSL or IPSEC with ESP.
Reinventing the wheel is probably not the best idea in this case.
Will add a note on this to my blog post for completeness.
For more information on IPsec see http://en.wikipedia.org/wiki/IPsec
would a nonce not work? You could just timestamp your request. And only accept requests in a certain timeframe.
http://www.thebuzzmedia.com/designing-a-secure-rest-api-with...
nonce is ok as long as for your application it is ok to expose the client to playback attack only for a limited time. I would go for a pre-request protocol where you perform a request to get a request-seed, and then use it in the next request (and the server will remove it when already used).
Another thing that makes sense is to use a nonce plus adding the IP address in the string to sign, with server side verification that the IP matches. So at least the playback attack needs to be performed in near time with the same IP as the originating client, that is considerably harder for the attacker to do.
A nonce when implemented correctly doesn't expose the client to replay attacks at all unless I'm missing something here. When requests come in, store the nonce + timestamp and ensure that requests with the same nonce + timestamp are only ever executed once.
I don't understand why this is a downside
"Cryptography is hard, a single character difference will result in a completely different value."
HMAC is designed to guarantee the integrity of the message. If a single character difference didn't result in a completely different value, the whole method would be completely flawed.
"snake case your header names (yes this is bad form) before presenting them to your code as the list of header names."
From RFC 2616, "Hypertext Transfer Protocol -- HTTP/1.1", §4.2, "Message Headers": Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive.
"Use content MD5 at both ends of the conversation." Yeah.. no: https://bugzilla.mozilla.org/show_bug.cgi?id=232030
Yes what I should have said was repeatable signatures are hard across large strings.
Having just worked on this with a few people implementing independent implementations everyone got at least one character wrong, and therefore a complete missmatch of signature.
For those involved this was pretty frustrating.
Right, it's not really a disadvantage. But I wouldn't say that it's a requirement, either. A hash where each input character only affects certain output characters is less than ideal, but it's not a critical flaw. You could make one at least as good as md5 if you had the desire to.
@Dylan16807 Maybe not a requirement, but this could cause information leakage in some scenarios. I'm also pretty confident I couldn't come up with a hashing scheme as good as MD5. I'll leave that to the crypto pros.
Well it depends on what your criteria are for 'better'. If you have three bcrypts glued together that each get character%3, there are no blatant flaws, it's just going to give you an oversized hash for the security level you get. And each character will affect only one third of the hash.
How does one implement a browser single-app JS client (Backbone, Angular etc) to access server API with HMAC authentication? The secret key will be exposed clearly in the JavaScript client.
Unless you can use per-user keys (for example, if you control the server API) you basically can't do it. This is the same problem that DRM faces: you can't give the user the keys to the car and prevent them from driving it.
Wow, that's a great analogy. I will use that next time I have to explain the flaws of client side security to somebody.
You give it an API key with limited access