Facebook Messenger 验证共享位置的 X-Hub 签名

Posted

技术标签:

【中文标题】Facebook Messenger 验证共享位置的 X-Hub 签名【英文标题】:Facebook Messenger Verify X-Hub-Signature of Shared Location 【发布时间】:2017-12-07 04:46:37 【问题描述】:

我正在向我的 Messenger 应用程序添加 X-Hub 验证。我是在这里了解到的:https://developers.facebook.com/docs/messenger-platform/webhook-reference

我已成功通过验证来处理简单的短信,但是当我发送位置而不是短信时,验证失败,因为我生成的哈希值与 Facebook 在标题中给我的哈希值不匹配。这是我的验证码:

module.exports.requestIsValid = function(event) 
    if (event['headers']['X-Hub-Signature']) 
        var sha     = event['headers']['X-Hub-Signature']
        var body    = JSON.stringify(event.body);
        return sha == `sha1=$crypto.createHmac('sha1', config.APP_SECRET).update(body).digest('hex')`;
    

    return false;   // this return is never called, I know that the first return is the one returning false when it should be true
 

以下是常规文本消息负载的示例:

"body":"object":"page","entry":["id":"1366222643461024","time":1499114399253,"messaging":["sender":"id ":"1582085681843981","recipient":"id":"1366222643461024","timestamp":1499114399084,"message":"mid":"mid.$cAASAZhi0_wRjO3OtbFdCi5lV2qe4","seq":52192," text":"Test"]],"method":"POST","principalId":"offlineContext_authorizer_principalId","headers":"X-Real-Ip":"173.252.88.182","X -Forwarded-For":"173.252.88.182","Host":"test.localtunnel.me","X-Forwarded-Proto":"https","X-nginx-Proxy":"true","Connection ":"close","Content-Length":"270","Accept":"/","Accept-Encoding":"deflate, gzip","Content-Type":" application/json","X-Hub-Signature":"sha1=0f51d788fe5f1111846097ad016728cdcd06029f","query":,"path":,"identity":"accountId":"offlineContext_accountId","apiKey" :"offlineContext_apiKey","caller":"offlineContext_caller","cognitoAuthenticationProvider":"offlineContext_cognitoAuthenticationProvider","cognitoAuthenticationType":"offlineContext_cognitoAuthenticationType","sou rceIp":"127.0.0.1","user":"offlineContext_user","userAgent":"","userArn":"offlineContext_userArn","stageVariables":,"isOffline":true

下面是一个带有位置的有效负载示例:

"body":"object":"page","entry":["id":"1366222643461024","time":1499114451619,"messaging":["sender":"id ":"1582085681843981","recipient":"id":"1366222643461024","timestamp":1499114451469,"message":"mid":"mid.$cAASAZhi0_wRjO3R6DVdCi8v9yqk0","seq":52196," attachments":["title":"Brandon 的位置","url":"https://l.facebook.com/l.php?u=https%3A%2F%2Fwww.bing.com%2Fmaps%2Fdefault.aspx%3Fv%3D2%26pc%3DFACEBK%26mid%3D8100%26where1%3D35.142236316764%252C%2B-106.53531087607%26FORM%3DFBKPL1%26mkt%3Den-US&h=ATOu8uYrLDiFl6wG8RVfhXvwkMl7uB_l2MHqB_uKLhk8qC9p1ua0EOLpGkznVX7Y8YfxSXP7vDuAR7swPmDCw1esH2bwKhNNsZKxVPC2ViC2AFMO_g&s=1&enc=AZMYxff8btvCZWHtzUR4oFL7K2Mg6nXM_O_tRXXL-L8z508UAOauiSRztoRvWdlGCWU1dNRdNK1ls2CGulM8lvzR","type":"location","payload":"coordinates":"lat":35.142236316764,"long ":-106.53531087607]]],"method":"POST","principalId":"offlineContext_authorizer_principalId』,"headers":"X-Real-Ip":"173.252.90.239", "X-Forwarded-For":"173.252.90.239","Host":"test.localtunnel.me","X-Forwarded-Proto":"https","X-Nginx-Proxy":"true", "Connection":"close","Content-Length":"911","Accept":"/","Accept-Encoding":"deflate, gzip","Content-Type" :"application/json","X-Hub-Signature":"sha1=34f23436b2744b9b0cc8776922e7386c454786db","query":,"path":,"identity":"accountId":"offlineContext_accountId"," apiKey":"offlineContext_apiKey","caller":"offlineContext_caller","cognitoA uthenticationProvider":"offlineContext_cognitoAuthenticationProvider","cognitoAuthenticationType":"offlineContext_cognitoAuthenticationType","sourceIp":"127.0.0.1","user":"offlineContext_user","userAgent":"","userArn":"offlineContext_userArn"," stageVariables":,"isOffline":true

如果需要,很乐意提供额外信息。

更新: 经过进一步检查,似乎只有在有效负载中存在“附件”字段时,验证才会失败。因此,如果我发送图片或 gif 或类似的东西时也会失败。

【问题讨论】:

您不应该自己对数据进行字符串化(即使是最微小的差异也会使哈希无效),而应该从您的应用程序首先收到的原始帖子正文创建哈希。 @CBroe 当我省略 stringify 时,我收到此错误 - TypeError: Data must be a string or a buffer。看起来它来自我正在使用的加密节点模块 【参考方案1】:

您应该将 SHA1 算法应用于“原始请求正文”。如果您解析 body 并将其转换回 json;它可能不起作用。

【讨论】:

如果你想按原样调试 Facebook webhook 请求,你可以使用 chatbotproxy.com 好吧,这是有道理的,不幸的是,当我省略 stringify 时,我得到了这个错误 - TypeError: Data must be a string or a buffer。看起来它来自我正在使用的加密节点模块 你可以把它串起来。它会起作用的。如果您不进行字符串化并将其发送到加密,它将引发该错误。看我的回答【参考方案2】:

你的bodyParserJSON应该返回rawBody

bodyParser.json(
    verify(req, res, buf) 
      req.rawBody = buf;
    ,
)

这是我编写的中间件。它使用crypto模块生成sha1

fbWebhookAuth: (req, res, next) => 
    const hmac = crypto.createHmac('sha1', process.env.FB_APP_SECRET);
    hmac.update(req.rawBody, 'utf-8');
    if (req.headers['x-hub-signature'] === `sha1=$hmac.digest('hex')`) next();
    else res.status(400).send('Invalid signature');

最后在您的路线中,您可以将其用作:

app.post('/webhook/facebook', middlewares.fbWebhookAuth, facebook.webhook);

【讨论】:

【参考方案3】:

如果你使用 express js

app.use(express.json(
verify:(req, res, buf)=>
  req.rawBody = buf;
))

你的中间件

const fbWebhookAuth = (req, res, next) => 
const hmac = crypto.createHmac('sha1', process.env.FB_APP_SECRET);
hmac.update(req.rawBody);
if (req.headers['x-hub-signature'] === `sha1=$hmac.digest('hex')`) next();
else res.status(400).send('Invalid signature'); 

你的端点

app.post('/webhook/Facebook', fbWebhookAuth, facebook.webhook);

【讨论】:

以上是关于Facebook Messenger 验证共享位置的 X-Hub 签名的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法使用 facebook SDK 在 facebook messenger 上共享链接

如何从 android 应用程序将纯文本共享到 facebook messenger

Facebook Messenger Bot 使用回发按钮共享结构化消息

Facebook Messenger 的 Webhook 不验证 Laravel5.4

来自 Facebook Messenger 机器人对话的 API 身份验证

托管 Facebook Messenger Bot 代码的最佳位置是啥? [关闭]