4

The following is an example taken from Facebook's authentication page. What is the idea behind adding data to the session and then redirecting to a URL using javascript? Also why do an md5 hash of a uniqid?

<?php 

   $app_id = "YOUR_APP_ID";
   $app_secret = "YOUR_APP_SECRET";
   $my_url = "YOUR_URL";

   session_start();
   $code = $_REQUEST["code"];

   if(empty($code)) {
     $_SESSION['state'] = md5(uniqid(rand(), TRUE)); //CSRF protection
     $dialog_url = "http://www.facebook.com/dialog/oauth?client_id=" 
       . $app_id . "&redirect_uri=" . urlencode($my_url) . "&state="
       . $_SESSION['state'];

     echo("<script> top.location.href='" . $dialog_url . "'</script>");
   }

   if($_REQUEST['state'] == $_SESSION['state']) {
     $token_url = "https://graph.facebook.com/oauth/access_token?"
       . "client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url)
       . "&client_secret=" . $app_secret . "&code=" . $code;

     $response = file_get_contents($token_url);
     $params = null;
     parse_str($response, $params);

     $graph_url = "https://graph.facebook.com/me?access_token=" 
       . $params['access_token'];

     $user = json_decode(file_get_contents($graph_url));
     echo("Hello " . $user->name);
   }
   else {
     echo("The state does not match. You may be a victim of CSRF.");
   }

 ?>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
James P.
  • 19,313
  • 27
  • 97
  • 155

3 Answers3

3

I know this will probably get slated as it is a wikipedia link, but you can find a full explanation of csrf here http://en.wikipedia.org/wiki/Cross-site_request_forgery, once you fully understand what it is you will understand how having a unique token per user can protect against it. The prevention section lists using a per-user token as a method of prevention.

Liam Bailey
  • 5,879
  • 3
  • 34
  • 46
  • Ok, so what csrf is about is piggybacking a user to get access to a site. Correct? I can understand the bit about an authenticated user visiting a page and unknowingly triggering a url. However, should the code above be inserted in every single header? It seems a bit heavy. – James P. Jul 11 '11 at 20:57
  • 1
    Basically if you have a site that initiates actions based on a post or a get request you should always authenticate where/how the request is coming. – Liam Bailey Jul 13 '11 at 13:16
1

It ensures that you are being redirected here only in response to an action initiated by the site. Read up on CSRF at https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29.

Femi
  • 64,273
  • 8
  • 118
  • 148
  • I see. So this would be a way to verify at Point B (arrival) that a call came from Point A (origin). Do you think this could be solved internally using PHP? If I was to check where a call came from it should be possible to see this in server variables and if falsified provide a unique id which can be passed from one page to another. – James P. Jul 11 '11 at 21:05
  • 1
    The key thing is it has to be a value that can't trivially be duplicated (hence the md5/uniqid call). If it is a fixed value then a dedicated attacker will be able to deduce it and craft a cross-site request with it baked in. The SESSION call ensures that it is a value that can't be anticipated. – Femi Jul 11 '11 at 21:13
1

By generating a hard (impossible) to guess value ans storing it in a session as well as sending it with a request, this script can verify if it was called by itself instead of somewhere else. somewhere else the hard to guess value would be unknwon and could thus not be supplied.

hoppa
  • 3,011
  • 18
  • 21
  • So from what you're saying this is a way of verifying if requests are coming from the expected page. Would this be used for every page or in just specific cases? – James P. Jul 11 '11 at 21:02
  • 1
    It depends on your application. It's a tradeoff between security and overhead. You might not care if someone submits to your contact form from outside until it is used by someone who thinks he's funny and spams the hell out of you with it. What you could do is fix this kind of thinks in an abstraction around forms so you only need to fix this once. In general I don't think you want people submitting forms on your domain from somewhere outside your domain. – hoppa Jul 12 '11 at 12:45