2014-09-10 93 views
6

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

我用下面的代码来验证传入网络挂接

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 

通过网络挂接传递的X-中心签名头看起来像这样

X-集线器签名哈希:SHA1 = 1cffc5d4c77a3f696ecd9c19dbc2575d22ffebd4

我传递的密钥和数据准确地按照GitHub的文档,但是哈希总是结束与众不同。

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

,这是我最有可能曲解

秘密的部分:这是与HTTP请求作为X集线器签名头中传递的可选字符串。该头部的值作为身体的HMAC十六进制摘要计算,使用秘密作为密钥。

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

回答

8

似乎不能使用缓冲区,但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'); 
} 
+2

似乎并没有为我工作 – ditoslav 2015-10-19 13:09:42

+2

非常重要的一提的是GitHub的hookshot *必须*有它的Content-Type设置为'应用程序/ json'。您将在webhook配置页面上找到这些设置。 https://github.com/MY_ORG/MY_REPO/settings/hooks/HOOK_ID#delivery-response – 2017-07-02 22:32:24

+0

像@MrClean的状态,使用application/json! – Mattis 2017-08-29 11:17:55

1

添加到Patrick's答案。使用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和简单===检查此线程here

crypto.timingSafeEqual在Node.js的V6.6.0

2

还加入了帕特里克的答案加入,我建议使用快递连同它的机身的解析器。 下面是一个完整的例子。这适用于Express 4.x,Node 8.x(写作时的最新版本)。

请替换YOUR_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'); 
+0

注意:修正了'req.body'字符串两次的错误。 – 2017-07-03 23:12:44