在 Javascript 中验证 Facebook signed_request 签名
Validate Facebook signed_request signature in Javascript
我正在使用 Classic ASP 构建 Facebook 主页应用。我一直无法匹配 Facebook 作为发布的第一部分传递到应用程序中的签名 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 的签名转换为十六进制或将我的签名转换为二进制以便进行 "apples-to-apples" 比较并获得匹配。
这是我的代码:
/* 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("_", "/"));
}
在查看有关编码的 crypto-js 文档后,我找到了解决方案。 crypto-js 提供的 de-/encoding 方法在 https://code.google.com/archive/p/crypto-js/ 底部的 'Encoders' 下列出(感谢 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
);
}
我最近为他们所需的用户数据删除 webhook 实现了这个。不再需要外部依赖:
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. I also have a repo setup 的翻译,带有测试。
我正在使用 Classic ASP 构建 Facebook 主页应用。我一直无法匹配 Facebook 作为发布的第一部分传递到应用程序中的签名 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 的签名转换为十六进制或将我的签名转换为二进制以便进行 "apples-to-apples" 比较并获得匹配。
这是我的代码:
/* 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("_", "/"));
}
在查看有关编码的 crypto-js 文档后,我找到了解决方案。 crypto-js 提供的 de-/encoding 方法在 https://code.google.com/archive/p/crypto-js/ 底部的 'Encoders' 下列出(感谢 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
);
}
我最近为他们所需的用户数据删除 webhook 实现了这个。不再需要外部依赖:
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. I also have a repo setup 的翻译,带有测试。