GitHub Webhook秘密从不validation

我正在使用GitHub webhook将事件传递给我的应用程序(GitHub的Hubot的一个实例),并使用sha1秘密进行保护。

我正在使用下面的代码来validation传入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 

在webhook中通过的X-Hub-Signature头看起来像这样

X-Hub-Signature:sha1 = 1cffc5d4c77a3f696ecd9c19dbc2575d22ffebd4

我按照GitHub的文档准确地传递了密钥和数据,但散列总是不一样。

这是GitHub的文档。 https://developer.github.com/v3/repos/hooks/#example

这是我最有可能误解的部分

secret:作为X-Hub-Signature标头与HTTP请求一起传递的可选string。 这个头部的值是作为身体的HMAChex摘要计算的,使用秘密作为密钥。

任何人都可以看到我要去哪里错了?

似乎不能使用缓冲区,但JSON.stringify(); 这是我的工作代码:

 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'); } 

加上帕特里克的答案。 使用crypo.timingSafeEqual比较HMAC摘要或秘密值是很好的。 就是这样:

 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'); } 

要更多地了解像timingEqual这样的安全比较和一个简单的===之间的区别,请点击此处 。

crypto.timingSafeEqual在Node.js v6.6.0中添加

也join了帕特里克的答案,我build议使用Express与它的身体分析器。 完整的例子如下。 这适用于Express 4.x,节点8.x(最新的写作)。

请replaceYOUR_WEBHOOK_SECRET_HERE ,并在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');