4

I have 2 nodejs microservices (let call them service x and service y). In service x, I will be making POST requests to service y.

Lets say service x will POST at this HTTPs endpoint in service y: /order/orderId

I think of service x as a webhooks producer, and service y to be webhooks consumer.

I want service y to be able to ensure that the message is coming from service x, and not from any malicious source. For this to work, I want to sign requests within service x before it makes POST request to service y.

What options do I have to sign these requests? How can I sign these reqeusts in service x, and how do I validate these requests in service y? Also what signing/encryption options do I have?

Concrete code examples in nodejs will be of great help.

3
  • What have you tried? What happened when you tried it? Commented Aug 22, 2021 at 21:04
  • I haven't tried anything yet, I am looking for a solution to securely process webhooks in service y --- which are generated/triggered from service x. @MattMorgan Commented Aug 22, 2021 at 21:07
  • 1
    The github webhook docs explain the process you need, and do so rather beautifully. docs.github.com/en/developers/webhooks-and-events/webhooks/… Commented Aug 22, 2021 at 22:59

1 Answer 1

4

You can use a secret key between the two services exclusively and use it to sign the requests.

In the sender (service x):

  • Append a custom header for the HTTP POST request, x-webhook-signature, which will be a generated sha256 HMAC using the secret key:
function createHmacSignature(req) {
  return require("crypto")
    .createHmac("sha256", secretKey)
    .update(JSON.stringify(req.body))
    .digest("hex");
}

In the receiver (service y):

  • Get the signature header:
req.headers["x-webhook-signature"]
  • Using the secret key, create the HMAC again as above.
  • Compare the resulted string with the x-webhook-signature header, if they match, the request origin would be the expected one.
function compareSignatures (signature, comparison_signature) {
  const source = Buffer.from(signature);
  const comparison = Buffer.from(comparison_signature);
  return require("crypto").timingSafeEqual(source, comparison);
}

Of course, extra validations and enhancements can be done, this is the normal flow to guarantee that only service x can send the request.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the response. If we are sharing a secret key between 2 services, do I still need to identify service x using x-public-key? Also, can I use any GUID value for a secret key? would it be a good choice? if not, how should I generate one? @Majed
No, if only x is expected to send the request there's no need for the public key. the secret key can be any random string, it's usually formatted to start with sk_ for production and sk_test_ for sandbox.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.