How to validate the X-Hub-Signature header when using Express.js and body-parser
X-Hub-Signature is the SHA1 hash of the raw request payload. When using Node.js, Express.js and body-parser, to get the raw request payload you have to use the body-parser verify function option. And if you want to control the response status code and body content when the verify function throws an error, then you have to use a error-handling middleware function.
Here is a full example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var express = require('express'); | |
var bodyParser = require('body-parser') | |
var crypto = require('crypto'); | |
// Calculate the X-Hub-Signature header value. | |
function getSignature(buf) { | |
var hmac = crypto.createHmac("sha1", process.env.FB_APP_SECRET); | |
hmac.update(buf, "utf-8"); | |
return "sha1=" + hmac.digest("hex"); | |
} | |
// Verify function compatible with body-parser to retrieve the request payload. | |
// Read more: https://github.com/expressjs/body-parser#verify | |
function verifyRequest(req, res, buf, encoding) { | |
var expected = req.headers['x-hub-signature']; | |
var calculated = getSignature(buf); | |
console.log("X-Hub-Signature:", expected, "Content:", "-" + buf.toString('utf8') + "-"); | |
if (expected !== calculated) { | |
throw new Error("Invalid signature."); | |
} else { | |
console.log("Valid signature!"); | |
} | |
} | |
// Express error-handling middleware function. | |
// Read more: http://expressjs.com/en/guide/error-handling.html | |
function abortOnError(err, req, res, next) { | |
if (err) { | |
console.log(err); | |
res.status(400).send({ error: "Invalid signature." }); | |
} else { | |
next(); | |
} | |
} | |
var app = express(); | |
var listener = app.listen(process.env.PORT ? process.env.PORT : 3000); | |
// body-parser is the first Express middleware. | |
app.use(bodyParser.json({ verify: verifyRequest })) | |
// Add an error-handling Express middleware function | |
// to prevent returning sensitive information. | |
app.use(abortOnError); | |
app.post('/webhook/', function (req, res) { | |
res.status(200).send("done!"); | |
}); |