当社のウェブサイトがFacebookがユーザーに署名されていると見ている場合、CookieにユーザーID 678678678がありますが、このCookieが偽造されていないことをどうやって知ることができますか?
-
25-09-2019 - |
質問
RESTまたはグラフAPIを使用してFacebookに電話をかけると、偽のセッション / auth_token / access_tokenと言って戻ってくるので、偽物かどうかがわかります。しかし、ユーザーの「最も優先される製品リスト」などの独自のDB情報を表示している場合は、Facebookに電話をかけず、DBのデータを表示します。誰かがクッキーを偽造しているのではなく、それが本当にユーザーであることをどうやって知るのでしょうか?
解決
FacebookでCookieを読むと、「Sig」と呼ばれる値が含まれています。この値、他のCookie値、およびアプリの秘密では、Cookieの内容をハッシュし、SIGに対して検証します。それらが一致する場合、Cookieは有効です。あなたとFacebookだけがアプリの秘密にアクセスできるので、この結果を信頼することができます。 FacebookのPHP SDKがどのように行うかの例は次のとおりです。立派なFacebook SDKは、あなたのために内部的にこれをすべて行います。
/**
* Validates a session_version=3 style session object.
*
* @param Array $session the session object
* @return Array the session object if it validates, null otherwise
*/
protected function validateSessionObject($session) {
// make sure some essential fields exist
if (is_array($session) &&
isset($session['uid']) &&
isset($session['access_token']) &&
isset($session['sig'])) {
// validate the signature
$session_without_sig = $session;
unset($session_without_sig['sig']);
$expected_sig = self::generateSignature(
$session_without_sig,
$this->getApiSecret()
);
if ($session['sig'] != $expected_sig) {
self::errorLog('Got invalid session signature in cookie.');
$session = null;
}
// check expiry time
} else {
$session = null;
}
return $session;
}
これがC#で同じことです(Facebook C#SDK):
/// <summary>
/// Validates a session_version=3 style session object.
/// </summary>
/// <param name="session">The session to validate.</param>
protected override void ValidateSessionObject(FacebookSession session)
{
if (session == null)
{
return;
}
var signature = this.GenerateSignature(session);
if (session.Signature == signature.ToString())
{
return;
}
session = null;
}
/// <summary>
/// Generates a MD5 signature for the facebook session.
/// </summary>
/// <param name="session">The session to generate a signature.</param>
/// <returns>An MD5 signature.</returns>
/// <exception cref="System.ArgumentNullException">If the session is null.</exception>
/// <exception cref="System.InvalidOperationException">If there is a problem generating the hash.</exception>
protected override string GenerateSignature(FacebookSession session)
{
var args = session.Dictionary;
StringBuilder payload = new StringBuilder();
var parts = (from a in args
orderby a.Key
where a.Key != "sig"
select string.Format(CultureInfo.InvariantCulture, "{0}={1}", a.Key, a.Value)).ToList();
parts.ForEach((s) => { payload.Append(s); });
payload.Append(this.ApiSecret);
byte[] hash = null;
using (var md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create())
{
if (md5 != null)
{
hash = md5.ComputeHash(Encoding.UTF8.GetBytes(payload.ToString()));
}
}
if (hash == null)
{
throw new InvalidOperationException("Hash is not valid.");
}
StringBuilder signature = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
signature.Append(hash[i].ToString("x2", CultureInfo.InvariantCulture));
}
return signature.ToString();
}
他のヒント
あなたが信頼できる唯一のことはです session_key
古いREST APIおよび access_token
グラフAPI用。取得したら、データの撤回要求でサーバー側に渡します。サーバー側でFacebook APIを呼び出し、現在のユーザーIDを取得します。ユーザーIDを取得したら、セッションに保存して後で使用できます。
クッキーに保管しないでください。セッション変数に入れてください。
ユーザーIDをCookieに入れないでください。セッションCookieは、サーバー側のセッションデータベースのレコードにマッピングする乱数である必要があります。そのセッションに関連付けられたデータは、サーバー側のみに保存されます。
そうすれば、セッションを偽造するために、攻撃者はその時点で実際に使用されている乱数を推測する必要があります。多くの乱数とセッションが期限切れになっていることを考えると、それはほとんど不可能です。
ここにはいくつかのアプローチがあります。
非効率的:認証された操作を実行するときはいつでも、FB Cookieをつかみ、その中のデータを使用してダミーAPI呼び出しを行い、アクセストークンが有効でユーザー(つまりgrab /me?fields = id)と一致することを確認します。
より効率的:初めてユーザー用のFB Cookieが表示されたときは、ユーザーのサーバー側のセッションにCookieを保存します(十分に苦労してから具体的なセッションIDがCookieでクライアントに渡されます)。
別のアプローチであり、サーバー側のセッション状態を必要としません。初めてユーザー用のFB Cookieを表示し、サーバーのみを使用してCookieを使用してCookieを使用し、その結果としてHashにCookieに保存します。次に、FB Cookieの有効なハッシュがあるかどうかを確認できます。もしそうなら、あなたはそれを信頼します。それ以外の場合は、検証に戻ります。