如何在 .NET 中使用 Facebook signed_request?

Row*_*man 5 c# asp.net-mvc facebook-javascript-sdk facebook-login asp.net-mvc-5

我使用 Facebook 作为我的 Web 应用程序 (ASP.NET MVC) 的登录提供程序。

我的登录方式类似于 StackOverflow 的另一篇文章How to secure authorize a user via Facebook's Javascript SDK。我也分享用户的担忧。

我的登录流程如下:

1. 用户按下登录按钮。

使用 Facebook 登录

2. 用户必须接受该应用程序。

接受应用程序

3. javascript 回调检索响应。

var authResponse = response.authResponse;
Run Code Online (Sandbox Code Playgroud)

返回的对象:

{
    accessToken: "...",
    expiresIn: 1234,
    signedRequest: "...",
    userID: "123456789"
}
Run Code Online (Sandbox Code Playgroud)

我听说我可以使用signed_request来验证用户的请求,但网上的所有示例都是针对 PHP 的我如何在 .NET 中执行此操作?

Dou*_*g S 5

将 Rowan 的答案编译成最终代码:

public static string DecodeSignedRequest(string signed_request)
{
    try
    {
        if (signed_request.Contains("."))
        {
            string[] split = signed_request.Split('.');

            string signatureRaw = FixBase64String(split[0]);
            string dataRaw = FixBase64String(split[1]);

            // the decoded signature
            byte[] signature = Convert.FromBase64String(signatureRaw);

            byte[] dataBuffer = Convert.FromBase64String(dataRaw);

            // JSON object
            string data = Encoding.UTF8.GetString(dataBuffer);

            byte[] appSecretBytes = Encoding.UTF8.GetBytes(app_secret);
            System.Security.Cryptography.HMAC hmac = new System.Security.Cryptography.HMACSHA256(appSecretBytes);
            byte[] expectedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(split[1]));
            if (expectedHash.SequenceEqual(signature))
            {
                return data;
            }
        }
    }
    catch
    {
        // error
    }
    return "";
}

private static string FixBase64String(string str)
{
    while (str.Length % 4 != 0)
    {
        str = str.PadRight(str.Length + 1, '=');
    }
    return str.Replace("-", "+").Replace("_", "/");
}
Run Code Online (Sandbox Code Playgroud)

谢谢罗文!


Row*_*man 4

是的,signed_request可用于验证传入的登录请求是否真实。如果您使用 Javascript 登录用户(例如通过AJAX),您可以使用signed_request 来确保数据不为假。

根据Parsing the Signed Request,有 3 个主要步骤,但我会更具体一些。

  1. 获取signed_request字符串并将其分成两个字符串。有一个句点字符(句号),它是分隔符
    • 字符串的第一部分(签名)是第二部分的哈希值。
    • 第二部分包含有关用户和请求的一些信息(用户 ID、时间戳)。
  2. The strings are in Base64, but cannot be decoded straight away.
    • They are Base64-URL-encoded which means that + and / characters have been replaced with URL-friendly - and _ characters. Replace - characters with + and _ characters with /.
    • The strings may not be fully Base64 padded. Base64 strings should be divisible by 4; pad the strings out as necessary.
  3. Hash the signature using HMAC (SHA256) using your app secret as the key and compare the result to the signature that was provided.

1. Split and decode

Code

string response = ""; // the signed_request

string[] split = response.Split('.');

string signatureRaw = FixBase64String(split[0]);
string dataRaw = FixBase64String(split[1]);

// the decoded signature
byte[] signature = Convert.FromBase64String(signatureRaw);

byte[] dataBuffer = Convert.FromBase64String(dataRaw);

// JSON object
string data = Encoding.UTF8.GetString(dataBuffer);
Run Code Online (Sandbox Code Playgroud)

FixBase64String()

static string FixBase64String(string str)
{
    string result = str;

    while (result.Length % 4 != 0)
    {
        result = result.PadRight(result.Length + 1, '=');
    }

    result = result.Replace("-", "+").Replace("_", "/");

    return result;
}
Run Code Online (Sandbox Code Playgroud)

2. Compare the hashes

byte[] appSecretBytes = Encoding.UTF8.GetBytes("my_app_secret_here");

HMAC hmac = new HMACSHA256(appSecretBytes);

byte[] expectedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(dataRaw));

bool areEqual = expectedHash.SequenceEqual(signature);
Run Code Online (Sandbox Code Playgroud)

If areEqual is true then you can be sure that the signed request is valid and has not been tampered with (assuming your app secret is secure).

Remember to keep your app secret secure, otherwise malicious users can do bad things.

  • 这段代码有一个错误。它错误地计算了“expectedHash”,因此最终的“areEqual”比较将失败。这行 `byte[] ExpectedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(dataRaw));` 需要更改为 `byte[] ExpectedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(split[1])) ;`。 (2认同)