Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GitHub Webhook Secret Never Validates

I'm using a GitHub webhook to pipe events to an application of mine (an instance of GitHub's Hubot) and it is secured with an sha1 secret.

I'm using the following code to validate hashes on incoming webhooks

crypto    = require('crypto')
signature = "sha1=" + crypto.createHmac('sha1', process.env.HUBOT_GITHUB_SECRET).update( new Buffer request.body ).digest('hex')
unless request.headers['x-hub-signature'] is signature
  response.send "Signature not valid"
  return

The X-Hub-Signature header passed through in the webhook looks like this

X-Hub-Signature: sha1=1cffc5d4c77a3f696ecd9c19dbc2575d22ffebd4

I am passing in the key and data accurately as per GitHub's documentation, but the hash always ends up different.

Here is GitHub's documentation. https://developer.github.com/v3/repos/hooks/#example

and this is the section which I am most likely misinterpreting

secret: An optional string that’s passed with the HTTP requests as an X-Hub-Signature header. The value of this header is computed as the HMAC hex digest of the body, using the secret as the key.

Can anyone see where I'm going wrong?

like image 309
lukerollans Avatar asked Sep 10 '14 14:09

lukerollans


People also ask

What is secret in GitHub webhook?

Secret. Setting a webhook secret allows you to ensure that POST requests sent to the payload URL are from GitHub. When you set a secret, you'll receive the X-Hub-Signature and X-Hub-Signature-256 headers in the webhook POST request.

How do you create a webhook secret?

Adding a deploy secretSelect Configuration in the project settings panel. Scroll down to the Webhook Deploy Secret section, then click Set Webhook Secret. Looker will automatically generate a secret token. You can use this automatically generated secret, or you can type in your own secret token.


2 Answers

Seems to not work with a Buffer, but JSON.stringify(); Here's my working code:

var
  hmac,
  calculatedSignature,
  payload = req.body;

hmac = crypto.createHmac('sha1', config.github.secret);
hmac.update(JSON.stringify(payload));
calculatedSignature = 'sha1=' + hmac.digest('hex');

if (req.headers['x-hub-signature'] === calculatedSignature) {
  console.log('all good');
} else {
  console.log('not good');
}
like image 84
Patrick Avatar answered Sep 22 '22 06:09

Patrick


Adding to Patrick's answer. It's good to use crypto.timingSafeEqual for comparing HMAC digests or secret values. Here's how:

const blob = JSON.stringify(req.body);  
const hmac = crypto.createHmac('sha1', process.env.GITHUB_WEBHOOK_SECRET);
const ourSignature = `sha1=${hmac.update(blob).digest('hex')}`;

const theirSignature = req.get('X-Hub-Signature');

const bufferA = Buffer.from(ourSignature, 'utf8');
const bufferB = Buffer.from(theirSignature, 'utf8');

const safe = crypto.timingSafeEqual(bufferA, bufferB);

if (safe) {
  console.log('Valid signature');
} else {
  console.log('Invalid signature');
}

To know more about the difference between a secure compare like timingEqual and a simple === check this thread here.

crypto.timingSafeEqual was added in Node.js v6.6.0

like image 40
Saugat Avatar answered Sep 21 '22 06:09

Saugat