I’m using Facebook’s server-side user authentication to attempt to log people in to our site. To prevent CSRF, Facebook recommends creating a random session state variable, pass it to FB, and FB then passes it back to compare the values to ensure no security issues. This seems to work fine in all browsers except Firefox. Firefox is sending the state variable, but it is not storing it upon return to the site. session_start() appears at the top of the document which is not quoted below as I stripped out some variables and other code for security reason.
if(empty($code)) {
$rand = md5(uniqid(rand(), TRUE)); // CSRF protection
$_SESSION['mbny_state'] = $rand;
$dialog_url = "https://www.facebook.com/dialog/oauth?client_id="
. $app_id . "&redirect_uri=" . urlencode($my_url) . "&state="
. $_SESSION['mbny_state'] . "&scope=publish_stream,user_birthday,user_hometown,email";
header('Location: ' . $dialog_url);
}
$state = $_REQUEST['state'];
$secret_state = $_SESSION['state'];
// Check session state to prevent hacking
if($secret_state == $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);
// store the access token for future requests
$_SESSION['access_token'] = $params['access_token'];
// define graph url allowing us to communicate with FB with user's account
$graph_url = "https://graph.facebook.com/me?access_token="
. $params['access_token'];
$user = json_decode(file_get_contents($graph_url));
}
else {
$error_message = 'An error has occurred. Your session ID does not match. Please exit and try again.<br /><br />' . $secret_state . ' :: ' . $state . ' :: ' . $_SESSION['test'];
}
Firefox makes it to the else displaying the error message. I have returned the value of $_SESSION[‘state’] later in the document, and it is coming up NULL.
Additionally, I added a test variable to the session, called test, and it was stored fine by Firefox. What am I missing?
Thanks in advance.
Edit: It appears as if Firefox is willing to store session variables outside of the if statement. If I set $_SESSION[‘myny_state’] prior to the if statement, it stores that value when FB returns back with the $code. The problem here is that the state definitely doesn’t match because it is being reset with every page call now that it is outside the if statement.
I’m losing hair as we speak.
After paying some really close attention to what was happening within Firefox, I noticed that Firefox was stripping the ‘www’ from the domain name even though the address originally included the ‘www.’ The session was then being saved to http://domain.com/whatever while the Facebook app was redirecting the user back to http://www.domain.com/whatever.
There are two ways to resolve this. I chose to fix this utilizing an .htaccess rewrite condition:
This could also be achieved by setting the session.cookie_domain as follows:
Hope someone finds this useful. Perhaps it’ll save you five hours of debugging something that is actually bug free.
As a side note, it’s considered good SEO practice to force your website to utilize either with the www or without the www – not both.