Skip to content

Commit ceaf8eb

Browse files
author
Ben Brown
authored
Merge pull request howdyai#555 from jonchurch/xhubverify
DDOS Vulnerability Fix - Secure Facebook Webhook
2 parents 86962ee + 3182544 commit ceaf8eb

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

facebook_bot.js

+7
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ if (!process.env.verify_token) {
7676
process.exit(1);
7777
}
7878

79+
if (!process.env.app_secret) {
80+
console.log('Error: Specify app_secret in environment');
81+
process.exit(1);
82+
}
83+
7984
var Botkit = require('./lib/Botkit.js');
8085
var os = require('os');
8186
var commandLineArgs = require('command-line-args');
@@ -99,6 +104,8 @@ var controller = Botkit.facebookbot({
99104
log: true,
100105
access_token: process.env.page_token,
101106
verify_token: process.env.verify_token,
107+
app_secret: process.env.app_secret
108+
validate_requests: true, // Refuse any requests that don't come from FB on your receive webhook, must provide FB_APP_SECRET in environment variables
102109
});
103110

104111
var bot = controller.spawn({

lib/Facebook.js

+37
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ var Botkit = require(__dirname + '/CoreBot.js');
22
var request = require('request');
33
var express = require('express');
44
var bodyParser = require('body-parser');
5+
var crypto = require('crypto')
56

67
function Facebookbot(configuration) {
78

@@ -359,6 +360,14 @@ function Facebookbot(configuration) {
359360
facebook_botkit.config.port = port;
360361

361362
facebook_botkit.webserver = express();
363+
364+
// Validate that requests come from facebook, and abort on validation errors
365+
if (facebook_botkit.config.validate_requests === true) {
366+
// Load verify middleware just for post route on our receive webhook, and catch any errors it might throw to prevent the request from being parsed further.
367+
facebook_botkit.webserver.post('/facebook/receive', bodyParser.json({verify: verifyRequest}))
368+
facebook_botkit.webserver.use(abortOnValidationError)
369+
}
370+
362371
facebook_botkit.webserver.use(bodyParser.json());
363372
facebook_botkit.webserver.use(bodyParser.urlencoded({ extended: true }));
364373
facebook_botkit.webserver.use(express.static(static_dir));
@@ -479,6 +488,34 @@ function Facebookbot(configuration) {
479488
}
480489
};
481490

491+
// Verifies the SHA1 signature of the raw request payload before bodyParser parses it
492+
// Will abort parsing if signature is invalid, and pass a generic error to response
493+
function verifyRequest(req, res, buf, encoding) {
494+
var expected = req.headers['x-hub-signature'];
495+
var calculated = getSignature(buf);
496+
if (expected !== calculated) {
497+
throw new Error("Invalid signature on incoming request");
498+
} else {
499+
facebook_botkit.debug('** X-Hub Verification successful!')
500+
}
501+
}
502+
503+
function getSignature(buf) {
504+
var hmac = crypto.createHmac("sha1", facebook_botkit.config.app_secret);
505+
hmac.update(buf, "utf-8");
506+
return "sha1=" + hmac.digest("hex");
507+
}
508+
509+
function abortOnValidationError(err, req, res, next) {
510+
if (err) {
511+
facebook_botkit.log('** Invalid X-HUB signature on incoming request!')
512+
facebook_botkit.debug('** X-HUB Validation Error:', err)
513+
res.status(400).send({ error: "Invalid signature." });
514+
} else {
515+
next();
516+
}
517+
}
518+
482519
return facebook_botkit;
483520
};
484521

readme-facebook.md

+9
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ Since Facebook delivers messages via web hook, your application must be availabl
5454

5555
When you are ready to go live, consider [LetsEncrypt.org](http://letsencrypt.org), a _free_ SSL Certificate Signing Authority which can be used to secure your website very quickly. It is fabulous and we love it.
5656

57+
## Validate Requests - Secure your webhook!
58+
Facebook sends an X-HUB signature header with requests to your webhook. You can verify the requests are coming from Facebook by enabling `validate_requests: true` when creating your bot controller. This checks the sha1 signature of the incoming payload against your Facebook App Secret (which is seperate from your webhook's verify_token), preventing unauthorized access to your webhook. You must also pass your `app_secret` into your environment variables when running your bot.
59+
60+
The Facebook App secret is available on the Overview page of your Facebook App's admin page. Click show to reveal it.
61+
62+
```
63+
app_secret=abcdefg12345 page_token=123455abcd verify_token=VerIfY-tOkEn node facebook_bot.js
64+
```
65+
5766
## Facebook-specific Events
5867

5968
Once connected to Facebook, bots receive a constant stream of events.

0 commit comments

Comments
 (0)