如何在NodeJS中比较请求体的HMAC SHA256签名

7
我正在尝试使用Dropbox API,并成功通过 Webhooks 收到了通知。现在,我想每次收到通知时都验证签名。
根据 Dropbox 的文档,他们写道:
“每个通知请求都将包括一个名为 X-Dropbox-Signature 的标头,其中包括请求正文的 HMAC-SHA256 签名,使用您的应用程序秘密作为签名密钥。这使得您的应用程序可以验证通知确实来自 Dropbox。”
因此,我成功获取了该签名,并使用 NodeJS 内置的 crypto 模块尝试创建自己的 HMAC SHA256 签名,然后将我的签名与 Dropbox 发送给我的签名进行比较。
以下是我用于执行此操作的代码:
  var sign = req.get("X-Dropbox-Signature");
  console.log(sign);
  var hmac = crypto.createHmac(algorithm, secret);
  hmac.update(JSON.stringify(req.body));
  hash = hmac.digest('hex');
  console.log(hash);

当算法为'sha256',密钥是我从我的Dropbox应用页面获取的秘密密钥时,我必须使用JSON.stringify(req.body),因为req.body是一个对象,而hmac.update需要一个字符串。我想知道这是否是导致错误的原因?

我在控制台记录签名(即来自Dropbox的签名),然后我记录了使用hmac创建的签名,但它是不同的签名。

有什么建议可以告诉我可能出了什么问题吗?


2
一眼看上去,JSON.stringify(req.body) 看起来很可疑。你需要对原始请求体进行 HMAC 处理,所以如果 req.body 是一个 Object,那可能不是正确的值。我建议查看 Web 框架的文档,看看如何仅检索原始/未处理的请求体。 - Greg
2个回答

5

Greg是正确的。您需要使用原始请求体来检查消息的成分。下面的代码使用body-parser库提取原始请求体。

var bodyParser = require("body-parser");

app.use(bodyParser.json({verify:function(req,res,buf){req.rawBody=buf}}))

然后针对POST方法:
app.post('/webhooks', function(req, res) {

    const retrievedSignature = req.get("X-header-Integrity")
    //send this body string for validation with secret
    const bodyString = Buffer.from(req.rawBody, 'utf8')

    let check = integrityCheck(retrievedSignature, bodyString, "secret")

});

太好了,但问题没有提到使用Express服务器...您能否添加一个不使用Express服务器且无法访问原始正文的示例? - MacK
1
我已经添加了一个使用Node的本地HTTP服务器的示例,但我不确定服务器如何无法访问原始请求正文。 - Andrew Dibble

2
以下片段是使用Node本地HTTP服务器计算请求体的HMAC的基本示例。相同的概念可以非常容易(并更加清晰)地在Express中间件中应用。
const http = require('http');
const crypto = require('crypto');

const APP_SECRET = 'prozr59vkis4454';
const REQUEST_BODY = '{"list_folder": {"accounts": ["dbid:AABL4QRrY7tB9viLgPUqmjkzE6Fe5ujlnlE"]}, "delta": {"users": [22575230]}}';
const SIGNATURE = 'aa2508fb90b757aa382edb0815c7f7df0ce1943c53f28fae96e1dc9eb7f677b1';

const server = http
  .createServer((req, res) => {
    const hmac = crypto.createHmac('sha256', APP_SECRET).setEncoding('hex');
    req
      .pipe(hmac)
      .on('finish', () => {
        console.log('signature from header:', req.headers['x-dropbox-signature']);
        console.log('calculated signature: ', hmac.read());
        res.writeHead(204);
        res.end();
      });
  })
  .listen(1313, () => {
    const request = http.request({
      host: 'localhost',
      port: 1313,
      method: 'POST',
    }, () => {
      server.close();
    });

    request.setHeader('x-dropbox-signature', SIGNATURE);

    request.write(REQUEST_BODY);
    request.end();
  });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接