0

Say that I have a big application for which I would like to create a secure API. The standard way to go would be to work with a public API key and a secret API key and provide these to the user. Next, in every API request, the messages are signed using a MAC on the secret key and (some part of) the message.

Now, since this application is a big one, I don't want to have to select my message field on a per-API-gateway basis (e.g. if my API request was for answering a question, message=answer, if my API request was for posting a comment message=comment etc...).

For convenience I would hence like to add a cryptographically safe random field, which is added upon every request (let's call this field our salt). This way I can write one request verification that covers all situations.

My questions is now:

Is this a good idea and why (not) so?

2

A unique (often random) element added to a message is called a nonce. It can be used as a defense against replay attacks (that is, an attacker who can observe a correctly signed legitimate request could later replay that exact request to cause the server to take the same action again). As such, it can be very useful (assuming you verify that a given nonce can indeed be used only once).

However, if I understand you correctly, you want to compute the MAC for the nonce/salt instead of for the actual message data. This will not work, as only the parts of the message that are included in the MAC are protected, of course. I suggest you serialize your entire message into a suitable format (e.g. JSON), compute the MAC over it, and send both in the request. That way, the MAC/signature is not a part of the message proper, and verification can be done without knowledge of the message's structure itself.

  • I see. How should the MAC be sent then, should it be added as a header (to not invalidate the request that was hashed)? – JohannesB May 7 '18 at 11:17
  • 1
    The exact way of transmission depends on your use case. I'd just send it right next to the (serialized) message. Assuming you're using JSON: {data: JSON.stringify(msg), signature: hmac(JSON.stringify(msg))}. Then for decoding, first check whether request.signature == hmac(request.data), if yes, go on unserializing the payload: var msg = JSON.parse(request.data). So signing wraps the original data, and validation unwraps it again. – Marc Schütz May 7 '18 at 11:33
  • For the nonce, couldn't I just use a timestamp for every request and require it to be within 48 hours of the current server timestamp? – JohannesB May 7 '18 at 12:06
  • With timestamps, there's a small chance they're not unique (multiple requests in a short time). If you want to be on the safe side, include both a nonce (for uniqueness) and a timestamp (for thwarting replays). But 48h is a long time, the shorter the better. In general, a timeout will always leave a small time window during which an replay is possible. If you want to be really sure, you would have to check whether the nonce is really unique, which is more complicated and would involve some database on the server. OTOH, depending on your use case, replay attacks might not even matter for you. – Marc Schütz May 7 '18 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.