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

Securing FB Graph API Requests #1170

Merged
merged 3 commits into from
Jan 18, 2018
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
17 changes: 17 additions & 0 deletions docs/readme-facebook.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Table of Contents
* [Silent and No Notifications](#silent-and-no-notifications)
* [Messenger code API](#messenger-code-api)
* [Attachment upload API](#attachment-upload-api)
* [Built-in NLP](#built-in-nlp)
* [Message Tags](#message-tags)
* [App Secret Proof](#app-secret-proof )
* [Running Botkit with an Express server](#use-botkit-for-facebook-messenger-with-an-express-web-server)

## Getting Started
Expand Down Expand Up @@ -570,6 +573,20 @@ var taggedMessage = {
bot.reply(message, taggedMessage);
```

## App Secret Proof

To improve security and prevent your bot against man in the middle attack, it's highly recommended to send an app secret proof :

```javascript
var controller = Botkit.facebookbot({
access_token: process.env.page_token,
verify_token: process.env.verify_token,
app_secret: process.env.app_secret,
require_appsecret_proof: true // Enable send app secret proof
});
```

More information about how to secure Graph API Requests [here](https://developers.facebook.com/docs/graph-api/securing-requests/)

## Use BotKit for Facebook Messenger with an Express web server
Instead of the web server generated with setupWebserver(), it is possible to use a different web server to receive webhooks, as well as serving web pages.
Expand Down
79 changes: 71 additions & 8 deletions lib/Facebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ function Facebookbot(configuration) {

var api_host = configuration.api_host || 'graph.facebook.com';

var appsecret_proof = getAppSecretProof(configuration.access_token, configuration.app_secret);

// Create a core botkit bot
var facebook_botkit = Botkit(configuration || {});

Expand Down Expand Up @@ -138,6 +140,10 @@ function Facebookbot(configuration) {
//Add Access Token to outgoing request
message.access_token = configuration.access_token;

if (facebook_botkit.config.require_appsecret_proof) {
message.appsecret_proof = appsecret_proof;
}

request({
method: 'POST',
json: true,
Expand All @@ -149,7 +155,6 @@ function Facebookbot(configuration) {
},
function(err, res, body) {


if (err) {
botkit.debug('WEBHOOK ERROR', err);
return cb && cb(err);
Expand Down Expand Up @@ -286,12 +291,18 @@ function Facebookbot(configuration) {
// so that we can use it before a bot is spawned!
facebook_botkit.getInstanceInfo = function(cb) {
return new Promise(function(resolve, reject) {
var uri = 'https://' + api_host + '/v2.6/me?access_token=' + configuration.access_token;

var instance = {
identity: {},
team: {},
};

request.get('https://' + api_host + '/v2.6/me?access_token=' + configuration.access_token,
if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof
}

request.get(uri,
{},
function(err, res, body) {
if (err) {
Expand Down Expand Up @@ -566,7 +577,13 @@ function Facebookbot(configuration) {
facebook_botkit.api.messenger_profile.getAPI('home_url', cb);
},
postAPI: function(message) {
request.post('https://' + api_host + '/v2.6/me/messenger_profile?access_token=' + configuration.access_token,
var uri = 'https://' + api_host + '/v2.6/me/messenger_profile?access_token=' + configuration.access_token;

if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.post(uri,
{form: message},
function(err, res, body) {
if (err) {
Expand All @@ -591,10 +608,17 @@ function Facebookbot(configuration) {
});
},
deleteAPI: function(type) {
var uri = 'https://' + api_host + '/v2.6/me/messenger_profile?access_token=' + configuration.access_token;

var message = {
'fields': [type]
};
request.delete('https://' + api_host + '/v2.6/me/messenger_profile?access_token=' + configuration.access_token,

if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.delete(uri,
{form: message},
function(err, res, body) {
if (err) {
Expand All @@ -605,7 +629,13 @@ function Facebookbot(configuration) {
});
},
getAPI: function(fields, cb) {
request.get('https://' + api_host + '/v2.6/me/messenger_profile?fields=' + fields + '&access_token=' + configuration.access_token,
var uri = 'https://' + api_host + '/v2.6/me/messenger_profile?fields=' + fields + '&access_token=' + configuration.access_token;

if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.get(uri,
function(err, res, body) {
if (err) {
facebook_botkit.log('Could not get messenger profile');
Expand All @@ -617,6 +647,8 @@ function Facebookbot(configuration) {
});
},
get_messenger_code: function(image_size, cb, ref) {
var uri = 'https://' + api_host + '/v2.6/me/messenger_codes?access_token=' + configuration.access_token;

var message = {
'type': 'standard',
'image_size': image_size || 1000
Expand All @@ -626,8 +658,11 @@ function Facebookbot(configuration) {
message.data = {'ref': ref};
}

request.post('https://' + api_host + '/v2.6/me/messenger_codes?access_token=' + configuration.access_token,
if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.post(uri,
{form: message},
function(err, res, body) {
if (err) {
Expand Down Expand Up @@ -660,13 +695,19 @@ function Facebookbot(configuration) {

var attachment_upload_api = {
upload: function(attachment, cb) {
var uri = 'https://' + api_host + '/v2.6/me/message_attachments?access_token=' + configuration.access_token;

var message = {
message: {
attachment: attachment
}
};

request.post('https://' + api_host + '/v2.6/me/message_attachments?access_token=' + configuration.access_token,
if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.post(uri,
{ form: message },
function(err, res, body) {
if (err) {
Expand Down Expand Up @@ -700,7 +741,13 @@ function Facebookbot(configuration) {

var tags = {
get_all: function(cb) {
request.get('https://' + api_host + '/v2.6/page_message_tags?access_token=' + configuration.access_token,
var uri = 'https://' + api_host + '/v2.6/page_message_tags?access_token=' + configuration.access_token;

if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.get(uri,
function(err, res, body) {
if (err) {
facebook_botkit.log('Could not get tags list');
Expand Down Expand Up @@ -735,9 +782,15 @@ function Facebookbot(configuration) {
},
postAPI: function(value, custom_token) {
var uri = 'https://' + api_host + '/v2.8/me/nlp_configs?nlp_enabled=' + value + '&access_token=' + configuration.access_token;

if (custom_token) {
uri += '&custom_token=' + custom_token;
}

if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.post(uri, {},
function(err, res, body) {
if (err) {
Expand Down Expand Up @@ -769,6 +822,11 @@ function Facebookbot(configuration) {
}
return new Promise(function(resolve, reject) {
var uri = 'https://' + api_host + '/v2.6/' + uid + '?fields=' + fields + '&access_token=' + configuration.access_token;

if (facebook_botkit.config.require_appsecret_proof) {
uri += '&appsecret_proof=' + appsecret_proof;
}

request.get(uri, {},
function(err, res, body) {
if (err) {
Expand Down Expand Up @@ -851,6 +909,11 @@ function Facebookbot(configuration) {
return 'sha1=' + hmac.digest('hex');
}

function getAppSecretProof(access_token, app_secret) {
var hmac = crypto.createHmac('sha256', app_secret || "");
return hmac.update(access_token).digest('hex');
}

function abortOnValidationError(err, req, res, next) {
if (err) {
facebook_botkit.log('** Invalid X-HUB signature on incoming request!');
Expand Down