Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #555 from jonchurch/xhubverify
Browse files Browse the repository at this point in the history
DDOS Vulnerability Fix - Secure Facebook Webhook
  • Loading branch information
Ben Brown authored Dec 20, 2016
2 parents 86962ee + 3182544 commit ceaf8eb
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
7 changes: 7 additions & 0 deletions facebook_bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ if (!process.env.verify_token) {
process.exit(1);
}

if (!process.env.app_secret) {
console.log('Error: Specify app_secret in environment');
process.exit(1);
}

var Botkit = require('./lib/Botkit.js');
var os = require('os');
var commandLineArgs = require('command-line-args');
Expand All @@ -99,6 +104,8 @@ var controller = Botkit.facebookbot({
log: true,
access_token: process.env.page_token,
verify_token: process.env.verify_token,
app_secret: process.env.app_secret
validate_requests: true, // Refuse any requests that don't come from FB on your receive webhook, must provide FB_APP_SECRET in environment variables
});

var bot = controller.spawn({
Expand Down
37 changes: 37 additions & 0 deletions lib/Facebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var Botkit = require(__dirname + '/CoreBot.js');
var request = require('request');
var express = require('express');
var bodyParser = require('body-parser');
var crypto = require('crypto')

function Facebookbot(configuration) {

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

facebook_botkit.webserver = express();

// Validate that requests come from facebook, and abort on validation errors
if (facebook_botkit.config.validate_requests === true) {
// 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.
facebook_botkit.webserver.post('/facebook/receive', bodyParser.json({verify: verifyRequest}))
facebook_botkit.webserver.use(abortOnValidationError)
}

facebook_botkit.webserver.use(bodyParser.json());
facebook_botkit.webserver.use(bodyParser.urlencoded({ extended: true }));
facebook_botkit.webserver.use(express.static(static_dir));
Expand Down Expand Up @@ -479,6 +488,34 @@ function Facebookbot(configuration) {
}
};

// Verifies the SHA1 signature of the raw request payload before bodyParser parses it
// Will abort parsing if signature is invalid, and pass a generic error to response
function verifyRequest(req, res, buf, encoding) {
var expected = req.headers['x-hub-signature'];
var calculated = getSignature(buf);
if (expected !== calculated) {
throw new Error("Invalid signature on incoming request");
} else {
facebook_botkit.debug('** X-Hub Verification successful!')
}
}

function getSignature(buf) {
var hmac = crypto.createHmac("sha1", facebook_botkit.config.app_secret);
hmac.update(buf, "utf-8");
return "sha1=" + hmac.digest("hex");
}

function abortOnValidationError(err, req, res, next) {
if (err) {
facebook_botkit.log('** Invalid X-HUB signature on incoming request!')
facebook_botkit.debug('** X-HUB Validation Error:', err)
res.status(400).send({ error: "Invalid signature." });
} else {
next();
}
}

return facebook_botkit;
};

Expand Down
9 changes: 9 additions & 0 deletions readme-facebook.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ Since Facebook delivers messages via web hook, your application must be availabl

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.

## Validate Requests - Secure your webhook!
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.

The Facebook App secret is available on the Overview page of your Facebook App's admin page. Click show to reveal it.

```
app_secret=abcdefg12345 page_token=123455abcd verify_token=VerIfY-tOkEn node facebook_bot.js
```

## Facebook-specific Events

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

0 comments on commit ceaf8eb

Please sign in to comment.