Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare HMAC SHA256 signatures from request body in NodeJS

I am trying to use Dropbox's API, and I got it to successfully send me alerts via webhooks, but now I want to verify the signatures every time they send me an alert.

From dropbox's documentation, they write:

"Every notification request will include a header called X-Dropbox-Signature that includes an HMAC-SHA256 signature of the request body, using your app secret as the signing key. This lets your app verify that the notification really came from Dropbox."

So I successfully catch that signature, and I use NodeJS built in crypto module to try to create my own signature with HMAC SHA256 and then compare my signature against the signature Dropbox sends me.

Here is my code for doing so:

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

Where algorithm is just 'sha256' and secret is my secret key that I got from my dropbox apps page. I have to use JSON.stringify(req.body) because req.body is an object and hmac.update takes a string. I am wondering if that is where my error comes from?

I console log the sign which is the signature from dropbox, and then I console log the signature which I created using hmac, but it is a different signature.

Any suggestions to what I may be doing wrong?

like image 806
skob002 Avatar asked Feb 17 '18 15:02

skob002


2 Answers

Greg is right. You need to use the raw body request to check the ingredients of the message. The following code employs the body-parser library to extract the raw body.

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

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

Then for the post method:

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")

});
like image 104
Syd Gillani Avatar answered Nov 17 '22 07:11

Syd Gillani


The following snippet is a basic example for calculating an HMAC of a request body using Node's native HTTP server. The same concepts can be very easily (and more cleanly) applied in an Express middleware.

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();
  });
like image 43
Andrew Dibble Avatar answered Nov 17 '22 08:11

Andrew Dibble