http://developers.facebook.com/docs/authentication/signed_request/

signed_requestパラメータは、Facebookとアプリケーションが色々な状況下で情報を共有する際に使われます

  • signed_requestはApps on Facebook.com に対して、Facebook上でロードされた時に渡されます。
  • ユーザがApp Dashboardからアプリケーションを削除したとき、Developer App のアプリケーション設定でDeauthorized Callbackが指定されていれば、アプリケーションにsigned_requestが送られます。
  • アプリケーションがRegistration Pluginを使っている場合、ユーザが登録するとsigned_requestが送られます。

signed_requestパラメータは、HMAC SHA-256 signatureとピリオドと base64urlでエンコードされたJSONオブジェクトです。以下の文字列のようなものです(実際には改行はありません。)
vlXgu64BQGFSQrY0ZcJBZASMvYvTHu9GQ0YM9rjPSso
.
eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0

HMAC signatureは、あなたが受け取るデータが本当にFacebookから送られていることを保証します。これはあなたのapplication secret(あなたとFacebookしか知り得ないもの)を用いて生成されています。このapplication secret無しには、サードパーティはsined_requestパラメータを改ざんするすることはできません。

signed_requestに含まれるJSONオブジェクトは以下のフィールドと情報を持っています。

NameDescription
userユーザのlocale,country,age(年齢層レンジのminとmax)オブジェクトを含むJSONオブジェクト
algorithmsignatureに用いたアルゴリズムを含むJSON文字列
issued_at生成された時点でのUnix timestampを含むJSON number
user_idユーザのFacebook usr identifier(UID)を含むJSON string
oauth_tokenGraph API もしくは Legacy REST APIに対して渡すoauth_token
expiresoauth_tokenが無効になる時点のUnix timestampを含むJSON number
app_dataクエリ文字列のコンテンツを含むJSON stringです。通常は、アプリケーション内部でアプリケーション自身へのリンクを貼ってデータを渡したい場合に用います。アプリケーションがPageタブに読み込まれるiframeである場合にのみ渡されます。
pageページのid、ユーザがページをlikeしたかの真偽値、ユーザが管理者であるかを示す真偽値を含むJSONオブジェクトです。アプリケーションがPageタブに読み込まれるiframeである場合にのみ渡されます。
profile_idアプリケーションを読み込んでいるページのIDを含むJSON number。アプリケーションがPageタブに読み込まれるFBMLである場合にのみ渡されます。
ユーザがアプリケーションを許可していない場合、アプリケーションは上記情報のサブセットのみ渡されます。


Verifying and Decoding

signatureを確かめてデータをデコードする手順はとても明解です。以下のPHPサンプルのようにします。
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);

// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);

if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return null;
}

// check sig
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}

return $data;
}

function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
$_REQUEST['signed_request']app secretを用いて上記の関数を実行します。 PHP SDKはパースしてsigned_requestを確認するところまで実行します。これを/tools/echoへ渡すことで簡単にデコード結果を見ることができます。

FAQ

JSONが数文字かけているせいでデコードできません
base64urlではなくbase64を用いている可能性があります。wikipediaをご覧ください
このsignatureのスキーマはどうなっていますか?
何種類ものsignatureスキーマが提案されてきました。OAuth 1.0はsignされる文字列を作り出しますが、正規化する過程が複雑で問題があります。fb_sigのスキーマはこの問題を簡単にしましたが、signする内容を正規化する方法が問題でした。これらの問題全てを解決するため、OAuth2.0の草案を基にしています。

なぜbase64urlエンコーディングを用いるのですか?
この場合、気をつけなくてはならない"奇妙な"文字は-_.ですが、HTTPヘッダやURLパラメータに含むには安全です。percent encodingや、改行の削除やブラウザの互換性に関してもう心配する必要はありません。
なぜsigned_requestと呼ばれるのですか?
signされるのはHTTPリクエストの一部です。signatureはパラメータの名前とパラメータ自身の最初に来ることを覚えておいてください。
なぜ"."で区切るのですか?signatureの長さは分からないのですか?
異なるアルゴリズムは異なる長さを持ちますし、それをデコードするまで、どのアルゴリズムが使われているかは特定できません。
なぜhexエンコーディングを使わないのですか?
一貫性があると言うのは良いことです。signatureと中身は今では全く同じ方法でエンコードされています。
なぜsignatureが最初に来るんですか?
left splitを用いるのは、right splitよりも一般的に簡単です。また、エンコーディングを使い分ける(たとえばsignatureはhexで中身はパーセントエンコードなど)ことを可能にしてくれます。 
例はありますか?
application secretはsecretに含まれ、データは{"0":"payload"}のようになります。