Announcement

Collapse
No announcement yet.

How to properly verify the signature in webhooks?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to properly verify the signature in webhooks?

    I'm trying to verify the authenticity of webhooks with the help of this guide:


    According to the guide, the signature is computed like this:
    $signature = base64_encode($webhookId . ':' . hash_hmac('sha256', $payload, $secretKey, true));​

    Our server is NodeJS based though, so I've rewritten this in TypeScript. I've tried various alternatives but I can't get the signatures match. I've made sure that I'm using the webhookId and secretKey found under Administration > Webhooks. I am using the payload verbatim without first converting it to

    Has someone been able to verify the signature? Or is there an easier way to verify the authenticity of the event?

    Here are the variations I've tried to generate a signature similar to what PHP would generate. All of them fail.

    ```
    export function generateSignature(webhookId: string, payload: string, secretKey: string): string {
    const hash = crypto.createHmac('sha256', secretKey).update(payload).digest('binary');
    const signature = Buffer.from(`${webhookId}:${hash}`).toString('base 64');
    return signature;
    }​
    ```
    ```
    export function generateSignature(webhookId: string, payload: string, secretKey: string): string {
    const hash = crypto.createHmac('sha256', secretKey).update(payload).digest('hex');
    const signature = Buffer.from(`${webhookId}:${hash}`).toString('base 64');
    return signature;
    }​
    ```

    This is what ChatGPT generated

    ```
    function generateSignature(webhookId: string, payload: string, secretKey: string): string {
    const hash = crypto.createHmac('sha256', secretKey).update(payload).digest();
    const signature = Buffer.from(`${webhookId}:${hash.toString('base64' )}`).toString('base64');
    return signature;
    }

    ​```

  • #2
    Here's where the signature header is generated: https://github.com/espocrm/espocrm/b...ender.php#L128

    To make sure that original values are the same you check against.

    Comment


    • #3
      Thank you very much for a quick reply. I figured the solution on my own though (a good night's sleep does wonders!)

      The key is to keep the hash as binary and do the concatenation with buffers directly.
      Here is the code that works if anyone else stumbles on this

      Code:
      export function generateSignature(webhookId: string, payload: string, secretKey: string): string {
          const hash = crypto.createHmac('sha256', secretKey).update(payload).digest();
          const signature = Buffer.concat([
              Buffer.from(webhookId + ':'),
              hash
          ]).toString('base64');
          return signature;
      }
      ​

      Comment

      Working...
      X