If you sign each request as described in the post Security Without SSL, Creating a Shared Secret to Sign Requests, you can be fairly confident that a passive MitM attacker won’t be able to modify the user’s request. However, they can potentially “replay” the request over and over again. Imagine if an attacker were able to replay Amazon’s 1-click purchase request over and over again and instead of paying for and getting 1 hard-drive like you wanted, you get 100. Without SSL, a passive attacker can replay the same post over and over again, and if you don’t defend against it, it can potentially create a lot of havoc that could ruin your relationship with your users. So, we need a way to prevent replay.
On the simple side, you can just have the client put a Request Time into each request. Then, when you receive the request on the server, you can validate that the time is within a reasonable grace period of the Request Time. If the request is older than the grace period allows, then you can reject the message. However, this relies on the user’s clock being set right, and a replay attack could still occur during the grace period.
So, the better way is to use a nonce. In some cases, the client requests a nonce from the server, and then is allowed to use that nonce one time. However, for this case, I’d say to do something easier. Since we already have a unique string (the signature we created from the shared secret), we can simply never allow the same signature to be used twice. This will reduce the overhead on the server and make the client’s life much easier since it won’t have to request any nonces. Then, since we’ll probably want to keep our nonce database small so that we aren’t looking through millions of records on each request, we can delete any records older than 24 hours or something. However, once we start deleting records, we need to do Request Time checking. If we keep records in the database for 24 hours, then we just need to ensure that all requests coming in are less than 24 hours old (or whatever timeframe we use to store nonces). This way, we can keep our database table of nonces smaller and still rest assured that a request from 5 days ago can’t be replayed ever again.
So, you may wonder what happens if the same user makes the same GET request twice in a row and produces the same signature hash. Well, obviously, the second request would get blocked. However, it seems that there is an overall application problem if the same person requests the same data twice within the same second. If the requests are even 1 second apart, the nonce will be completely different because the RequestTime in the request would change. However, if you’re still worried about it, just add a milliseconds field to the request, the server doesn’t even have to know what the field is for, it just has to make the request signature unique.
As far as other legitimate requests, the odds of producing the same signature on two different requests (hash collision) with a hashing algorithm like SHA256 or better that produces at least 64 characters is so vanishingly small that I would place more bets on you being the first to create FTL warp drive. So, I wouldn’t worry about it. Worst case scenario, the client script will have to make a second request when the first fails, and the time will have changed by then, and there won’t be another hash collission until our sun dies out 5 billion years from now.
Kevin Nelson February 9th, 2015
Posted In: Security