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

DDOS Vulnerability Fix - Secure Facebook Webhook #555

Merged
merged 8 commits into from
Dec 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -355,6 +356,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 @@ -475,6 +484,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