验证Javascript中的Facebook signature_request签名

时间:2018-08-15 15:20:58

标签: javascript facebook sha256 hmac

我正在使用Classic ASP构建一个Facebook Page应用程序。我一直无法匹配Facebook作为POST signed_request的第一部分传递给应用程序的签名。

由于VBScript中的密码学库很少,因此我使用的是服务器端Javascript和https://code.google.com/archive/p/crypto-js/中的crypto-js库

我已经尝试将Facebook https://developers.facebook.com/docs/games/gamesonfacebook/login#parsingsr上的文档中的PHP代码示例转换为Javascript。我可以生成signed_request有效载荷的HMAC SHA256哈希,但是与signed_request签名不匹配。

我认为问题在于Facebook的签名格式不同。它看起来是二进制(〜1抚慰..... ),而我正在生成的HMAC SHA256哈希是十六进制字符串( 7f7e8f5f ..... >)。在Facebook的PHP示例中,hash_hmac函数使用原始二进制参数。因此,我认为我需要将Facebook的签名转换为十六进制,或者将我的签名转换为二进制,以便进行“苹果到苹果”比较并获得匹配。

这是我的代码:

/* Use the libraries from https://code.google.com/archive/p/crypto-js/
crypto-js/crypto-js.min.js
crypto-js/hmac-sha256.min.js
crypto-js/enc-base64.min.js
*/

var signedRequest = Request.queryString("signed_request")

var FB_APP_SECRET = "459f038.....";

var arSR = signedRequest.split(".");
var encodedSig = arSR[0];
var encodedPayload = arSR[1];

var payload = base64UrlDecode(encodedPayload);
var sig = base64UrlDecode(encodedSig);

var expectedSig;

expectedSig = CryptoJS.HmacSHA256(encodedPayload, FB_APP_SECRET); // Unaltered payload string; no match
expectedSig = CryptoJS.HmacSHA256(payload, FB_APP_SECRET); // base64-decoded payload string; no match

if (sig == expectedSig) {
    Response.write(payload);
} else {
    Response.write("Bad signature");
}

function base64UrlDecode(input) {
    // Replace characters and convert from base64.
    return Base64.decode(input.replace("-", "+").replace("_", "/"));
}

2 个答案:

答案 0 :(得分:0)

研究了有关编码的crypto-js文档之后,我找到了解决方案。 https://code.google.com/archive/p/crypto-js/底部的“编码器”下列出了crypto-js提供的解编码方法(感谢微调,CBroe。)

解决方案是在签名上使用.toString()。似乎crypto-js使用阻止比较匹配的单词格式。我也转而使用crypto-js提供的base64解码,以便坚持使用一个库。

这是我更新的代码:

/* Use the libraries from https://code.google.com/archive/p/crypto-js/
crypto-js/crypto-js.min.js
crypto-js/hmac-sha256.min.js
crypto-js/enc-base64.min.js
*/

var signedRequest = Request.queryString("signed_request")

var FB_APP_SECRET = "459f038.....";

var arSR = signedRequest.split(".");
var encodedSig = arSR[0];
var encodedPayload = arSR[1];

var payload = base64UrlDecode(encodedPayload);
var sig = base64UrlDecode(encodedSig);

var expectedSig = CryptoJS.HmacSHA256(encodedPayload, FB_APP_SECRET); /******** Correct payload */

if (sig.toString() != expectedSig.toString()) { /******* Use .toString() to convert to normal strings */
    Response.write(payload);
} else {
    Response.write("Bad signature");
}

function base64UrlDecode(input) {
    return CryptoJS.enc.Base64.parse( /******** Decode */
        input.replace("-", "+").replace("_", "/") // Replace characters
    );
}

答案 1 :(得分:0)

我最近针对其所需的用户数据删除网络挂钩实现了此功能。不再需要外部依赖项:

const crypto = require('crypto');

function parseSignedRequest(signedRequest, secret) {
  const [signatureReceived, encodedPayload] = signedRequest.split('.', 2);
  const payload = b64decode(encodedPayload)
  const data = JSON.parse(payload);

  const hmac = crypto.createHmac('sha256', secret).update(payload);
  const expectedSignature = hmac.digest('base64');

  if (signatureReceived === expectedSignature) {
    return data;
  } else {
    throw new Error("Signature mismatch");
  }
}

function b64decode(data) {
  const buff = Buffer.from(data, 'base64');
  return buff.toString('ascii');
}

这是their example PHP code的翻译。我也有a repo setup的测试。