Verifying The Signature

From Facebook Developer Wiki

Jump to: navigation, search

Contents

[edit] Why Do You Need to Verify a Signature?

When Facebook sends you information about a user, your server needs to know that it is actually coming from Facebook. If you send a request to Facebook and receive a response, then you know that it comes from the right person because you know who you asked. But if a request comes in unsolicited, then you need to be able to authenticate that the user ID is actually coming from Facebook servers. Otherwise, some random Joe on the internet could send a request claiming to be user ID 2901279, and you would send data about that user.

The key to verification is the application secret. There are only two parties that know the secret: your application server, and Facebook. Whenever Facebook sends data to your server, it includes a signature that is encoded using your application secret. You can perform the same encoding, and check the signature to make sure it matches. Random Joe can't generate that signature because he doesn't know your secret key.

[edit] Verifying the Facebook User

Your client library should perform all the necessary validation for you. The official PHP client library does it when you instantiate the Facebook client:

$fb = new Facebook($api_key, $secret)


But you should then call get_loggedin_user() to confirm that the user is valid. See below:

$uid=$fb->get_loggedin_user(); if($uid) { //verified } else { //not verified !!! }


Depending the type of application you are using, the signature and parameters can come from different sources.

  • In an FBML canvas page on Facebook, the information is sent in a POST variable to your application.
  • In an iframe canvas page, it is sent via GET (since we can't post to an iframe).
  • In a Facebook Connect application on an external website, the information is sent via an iframe mechanism to JavaScript. The JavaScript library sets some cookies, which are sent by the browser to your server on every request. The cookies contain the signature and parameters.

[edit] Generating the Signature

When the parameters are sent via GET or POST, they have an "fb_sig" prefix attached. For instance, if you look at the $_POST variable in an FBML canvas page, you may see:

Array ( [fb_sig_app_id] => 12558585366 [fb_sig_in_canvas] => 1 [fb_sig_request_method] => GET [fb_sig_friends] => 4,6,... (removed my friend list for privacy) [fb_sig_position_fix] => 1 [fb_sig_locale] => en_US [fb_sig_in_new_facebook] => 1 [fb_sig_time] => 1221071115.1896 [fb_sig_added] => 1 [fb_sig_profile_update_time] => 1220998418 [fb_sig_user] => 2901279 [fb_sig_session_key] => 9a7e04226b1a3c85823bfafd-2901279 [fb_sig_expires] => 0 [fb_sig_api_key] => 650503b8455d7ae1cd4524da50d88129 [fb_sig] => 3221a15c4e2804c04da31670a7b64516 )


All of the parameters are those arguments that begin with "fb_sig". The signature is $_POST['fb_sig'].

To generate the signature for these arguments:

  1. Remove the "fb_sig_" prefix from all of the keys.
  2. Sort the array alphabetically by key.
  3. Concatenate all key/value pairs together in the format "k=v" (omitting the signature itself, since that is what we are calculating).
  4. Append your secret key, which you can find by going to the Developers application and following the link for your application.
  5. Take the md5 hash of the whole string.

To calculate the hash using the openssl command line tool, you could run this at a UNIX prompt:

echo -n "added=1api_key=650503b8455d7ae1cd4524da50d88129expires=0friends=4,6,...in_canvas=1in_new_facebook=1locale=en_USposition_fix=1profile_update_time=1220998418request_method=GETsession_key=9a7e04226b1a3c85823bfafd-2901279time=1221071115.1896user=290127986cd871c996910064ab9884459c58bab" | md5


Which prints:

3221a15c4e2804c04da31670a7b64516


This matches the value of the $_POST['fb_sig'] parameter, which validates the result as coming from Facebook.


Here is some PHP code that will do all of this for you.

<?php /* typical use case: process.php?string=$string&sig=fb_sig in process.php, $string .= $secret and md5($string) == $_GET['fb_sig'] */ $fbsig = array(); foreach($_GET as $key=>$value) { if(substr($key,0,7) == 'fb_sig_') { $fbsig[substr($key,7)] = $value; } } ksort($fbsig); foreach($fbsig as $key=>$value) { $string .= $key . '=' . $value; } $string .= $secret; md5($string) == $_GET['fb_sig']; // this will return true. ?>


And here is some C# code that will do all of this for you.

private bool VerifySignature(System.Collections.Specialized.NameValueCollection nameValueCollection) { string signature = nameValueCollection["fb_sig"]; if (String.IsNullOrEmpty(signature)) return false; string s = (from key in nameValueCollection.AllKeys where key.StartsWith("fb_sig_") orderby key select key.Substring(7) + "=" + nameValueCollection[key]) .Append() + facebookApi.Secret; StringBuilder computedSignature = new StringBuilder(); MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(s)).Action(b => computedSignature.AppendFormat("{0:x2}", b)); return computedSignature.ToString().ToLowerInvariant() == signature.ToLowerInvariant(); } public static string Append(this IEnumerable<string> list) { StringBuilder sb = new StringBuilder(); list.Action(s => sb.Append(s)); return sb.ToString(); }

usage: VerifySignature(HttpContext.Request.QueryString);

[edit] Signatures and Facebook Connect Sites

On Facebook Connect sites, there is no direct communication between Facebook servers and your servers. All communication is mediated by the user's browser via Cross Domain Communication. But Facebook has the same problem: we need to prevent the user from intercepting the communication and spoofing another user's ID.

In this case, the same signature validation is applied, except it happens via cookies set in JavaScript. When the JavaScript client receives information from Facebook, it sets cookies on the app domain containing all the signature parameters and the signature itself. Instead of a prefix of "fb_sig", we use a prefix of the application key.

FireCookie is a great tool for inspecting cookies as they go across. When I log into The Run Around, these cookies show up in my browser:

Image:Firecookie_connect.png

Let's go over what they each mean:

APIKEY_user
The user ID of the currently logged in user.
APIKEY_session_key
The current session. This is used to make API requests.
APIKEY_expires
When the current session expires. This is usually an hour or two after it's granted. If it's 0, then it means the session does not expire.
APIKEY_ss
The session secret. This prevents someone who knows your session key from using the session.
APIKEY (with nothing after it)
The signature (created using the application secret), which will be generated from all of the other parameters.
fbsetting_APIKEY
The last cookie is not related to the signature validation (which is why it does not start with the APIKEY prefix). It is used to cache the login state between page loads, so that the XFBML rendering does not have to wait for a round trip to Facebook before starting.

Any cookie that starts with APIKEY_ is part of the signature parameters. To verify the signature, use the same algorithm described above. As before, strip the prefix ("APIKEY_"), and omit the signature itself (the parameter just named with your APIKEY). Also, the "fbsetting_" cookie shouldn't be included when calculating the signature. Note that, again, as before, the application secret is necessary to match the hash, which means that *all* signature validation should be done on the server side, to avoid exposing the application secret.

[edit] About the Cookies

In the screen shot above, this is the array of values that is extracted from the cookies:

Array ( [expires] => 1221157773 [session_key] => 67bc4aa090e0d34954c1146b-2901279 [ss] => 7fe9f4fe1035ea92466975fa94176763 [user] => 2901279 )


The signature (signed using the app secret) for the above parameters is "f2ba692365925150d2f733bd0b8257c5", the value stored in the cookie denoted solely by APIKEY. If proper validation is desired, this can be sent to the server side along with the parameters, and the signature can be verified similarly to regular parameter validation for applications as described above.

[edit] Supported Libraries

The official PHP library supports all of these methods of authentication verification. Because they were written primarily for use on canvas pages, libraries for other languages may not work as well with the cookie authentication required by Facebook Connect. Please check with the library writer if you find that yours does not check the cookie-based signature validation.

[edit] See Also

reference