GitHub Webhook Secret never checks

I use the webhook GitHub to hook events to my application (an instance of the GitHub Hubot) and it is protected by the sha1 secret.

I use the following code to check hashes on incoming webcams

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 the webhook is as follows

X-Hub-Signature: sha1 = 1cffc5d4c77a3f696ecd9c19dbc2575d22ffebd4

I pass the key and data exactly as per the GitHub documentation, but the hash always ends up different.

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

and this is the section that I most likely misinterpret

secret: optional string passed with HTTP requests as the X-Hub-Signature header. The value of this header is calculated as the HMAC body hexadecimal digest, using the secret as the key.

Can anyone see where I'm wrong?

+6
source share
3 answers

It does not seem to work with the buffer, but JSON.stringify (); Here is 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'); } 
+8
source

Also adding Patrick to the answer, I recommend using Express with body-parser along with it. Full example below. This works with Express 4.x, Node 8.x (the last since it was written).

Please replace YOUR_WEBHOOK_SECRET_HERE and do something in authorizationSuccessful .

 // Imports const express = require('express'); const bodyParser = require('body-parser'); const crypto = require('crypto'); const app = express(); // The GitHub webhook MUST be configured to be sent as "application/json" app.use(bodyParser.json()); // Verification function to check if it is actually GitHub who is POSTing here const verifyGitHub = (req) => { if (!req.headers['user-agent'].includes('GitHub-Hookshot')) { return false; } // Compare their hmac signature to our hmac signature // (hmac = hash-based message authentication code) const theirSignature = req.headers['x-hub-signature']; const payload = JSON.stringify(req.body); const secret = 'YOUR_WEBHOOK_SECRET_HERE'; // TODO: Replace me const ourSignature = `sha1=${crypto.createHmac('sha1', secret).update(payload).digest('hex')}`; return crypto.timingSafeEqual(Buffer.from(theirSignature), Buffer.from(ourSignature)); }; const notAuthorized = (req, res) => { console.log('Someone who is NOT GitHub is calling, redirect them'); res.redirect(301, '/'); // Redirect to domain root }; const authorizationSuccessful = () => { console.log('GitHub is calling, do something here'); // TODO: Do something here }; app.post('*', (req, res) => { if (verifyGitHub(req)) { // GitHub calling authorizationSuccessful(); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Thanks GitHub <3'); } else { // Someone else calling notAuthorized(req, res); } }); app.all('*', notAuthorized); // Only webhook requests allowed at this address app.listen(3000); console.log('Webhook service running at http://localhost:3000'); 
+2
source

Add Patrick to the answer . It is good to use crypo.timingSafeEqual to compare 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 learn more about the difference between a secure timingEqual type comparison and simple ===, check out this thread here .

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

+1
source

Source: https://habr.com/ru/post/975070/


All Articles