The "state" param from the URL and session do not match - facebook

In facebook documantion
require('include/facebook/autoload.php'); //SDK directory
$fb = new Facebook\Facebook([
'app_id' => '***********',
'app_secret' => '***********************'
]);
$helper = $fb->getRedirectLoginHelper();
$permissions = ['email', 'public_profile']; // optional
$loginUrl = $helper->getLoginUrl('http://www.meusite.com.br/login-callback.php', $permissions);
When direct it to the url $loginUrl, the return is:
Facebook SDK returned an error: Cross-site request forgery validation failed. The "state" param from the URL and session do not match

I had the same error.
The problem occurred because I did getLoginUrl(...) before getAccessToken()
So rid of getLoginUrl(...) in redirected URL and code should works.

I had the same issue and for me that error was occurring because I did not put session_start(); in my login.php page code before calling getLoginUrl(..) and also at the top of login-callback.php page.
Just put session_start(); in your "login" page and "login-callback" page and it will work surely just like it is working for me now.

There could be 2 reason for this error:
you didn't call session_start(); before getLoginUrl call
You executed getLoginUrl again in login-callback.php, so state value regenerated and mismatched with the redirected value

Possible Fixes : I used the following configuration settings .
Enable WebAuthLogin under the advanced tab . Provide the url in the WebAuthLogin settins as same as that you provide in $loginUrl ;
For example if you use $loginUrl as https://example.com/ use that same in the WebAuthlogin Url
$loginUrl = $helper->getLoginUrl('https://example.com/', $permissions);

This problem occures also in case that you generate 2 or more login links on the same page (e.g. one for login and other for registration - even both point to the same url, they have just different labels).
Facebook SDK creates/updates $_SESSION[FBRLH_state] for each new generated loginURL. So if there are 2 generated URLs (using $helper->getLoginUrl()) then the $_SESSION[FBRLH_state] is 2-times rewritten and valid only for the last generated URL. Previous login URL becomes invalid. It means that it is not possible to generate 2 valid loginURLs. In case that 2 same URLs are generated then return the first one and avoid call of Facebook SDK for generation of second one.

I had the same problem.
The reason for this error is because --->
When "$helper->getLoginUrl" calls, it create a session variable "FB_State", and this is something to FB uses to match the token. Every-time getLoginUrl calls, it create new state. Then after user authorized and redirect back, if you codes cannot detect this event and re-run "$helper->getLoginUrl", then this error will occur.
The solution ->
refine your coding, stop run "$helper->getLoginUrl" again if authorized.
if you already rerun, then set the session variable for the token to NULL if you have, then User can re-authorize again.
when user tries re-authorize, they can remove the authorized APP once or you need to generate new link with "$helper->getReRequestUrl"
Yet, token has be called by "getAccessToken()" before the "$helper->getLoginUrl" or "$helper->getReRequestUrl" runs.
Good Luck!!!!!

Finally, looking into FB code, I discovered that the problem "Cross-site request forgery validation failed. Required param “state” missing" and similars are caused by PHP variable $_SESSION['FBRLH_state'] that for some "strange" reason when FB call the login-callback file.
To solve it I store this variable "FBRLH_state" AFTER the call of function $helper->getLoginUrl(...). Is very important to do only after the call of this function due to is inside this function when the variable $_SESSION['FBRLH_state'] is populated.
Below an example of my code in the login.php:
$uri=$helper->getLoginUrl($uri, $permissions);
foreach ($_SESSION as $k=>$v) {
if(strpos($k, "FBRLH_")!==FALSE) {
if(!setcookie($k, $v)) {
//what??
} else {
$_COOKIE[$k]=$v;
}
}
}
var_dump($_COOKIE);
And in the login-callback.php before calling all FB code:
foreach ($_COOKIE as $k=>$v) {
if(strpos($k, "FBRLH_")!==FALSE) {
$_SESSION[$k]=$v;
}
}
Last, but not least, remember also to include code for PHP session so..
if(!session_id()) {
session_start();
}
...
...
...
...
<?php session_write_close() ?>
I hope this response can help you to save 8-10 hours of work :)
Bye, Alex.

This issue was a bit confusing for me, because I had to change a line at the facebook src file:
src/Facebook/Helpers/FacebookRedirectLoginHelper.php
at the function: "validateCsrf" like this:
if ($result !== 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
And change it into:
if ($result === 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
I don't know if this makes a violation to the facebook SDK security, so I truly opened to any exlanation or recommendation for this answer.
You may also make the following changes at the facebook app manager:
add your site and callback-url into your facebook app account at:
setting->advanced:Valid OAuth redirect URIs
Don't forget to add another url with slash (/) at the end of each url and check all 4 checkboxes at Client OAuth Settings.

I had the same error. Are you using 1 file or 2? I was trying to get by using 1 file but my error was resolved when I split into login.php & fb-callback.php as the documentation recommended. My sessions were being re-written so the state was never saved properly.
Good luck!

Happens when the session in missing a needed variable.
might be caused by several things.
In my case I left the "www" out of the callback URL

You could actually be parsing the data from another domain... for example:
website.com is different from www .website.com
If you're parsing data from http ://website.com/login.php to http://www.website.com/fb-callback.php this would be a cross-domain problem and the error you are receiving would be because of that....
http ://website.com and http ://www.website.com are the same but the script identifies them as different..... hope that gives insight to the problem.

Related

Facebook access tokens format

I am manually building a login system to my application using facebook. Following this tutorial I am told to do some get requests to obtain user information. Echoing an returned token I obtain this
(using FILE_GET_CONTENTS() ):
$access_token = "graph.facebook.com/oauth/access_token?client_id=$client_id&redirect_uri=$redirect_uri&client_secret=$client_secret&code=$user_code") :
echoing $access_token:
access_token = CAAKlMMCiHp(...)ZByh3faLzh &expires=5125282
or this for an app_token ("graph.facebook.com/oauth/access_token?client_id=$client_id&client_secret=$client_secret&grant_type=client_credentials") :
echoing $app_token:
access_token = 744(...)90|vZU2(...)e0-s
The last one to obtain the user information that I have to do is this:
$last_one = "graph.facebook.com/debug_token?access_token=$access_token&app_token=$app_token";
Check above that I have to use $access_token and app_token. The problem is that when I echo $access_token what I obtain is literally access_token = xxxx..., including the "access_token" and the "=" sign instead of just the token.
So, when I try to do my last request ($last_one) I have (with variable replacement):
$last_one = "graph.facebook.com/debug_token?access_token=access_token=xxx&app_token=app_token=xxxx";
To avoid this I removed the first "access_token=" and "app_token=" and successfully eliminated the duplicate. Unfortunately I now have access_token=xxx&access_token=xxx because although one is literally for access and another is to the app both are treated as access_token and their echo also explicits "access_token =" as shown above.
My question is if this is supposed to happen and how can I get only the token? I find it kind of ridiculous to have the need of using substr() just to eliminate the first part of the token "access_token =". Am I supposed to reach the token by a getter part of file_get_contents()? And should I include &expires=xxxx of the first token in the last request? Please refer and shortly read the tutorial from "Confirming identities" to understand what is my dilema. I hope I made myself through, thank you very much!

CSRFGuard - request token does not match session token

I am trying to incorporate the CSRFGuard library in order to rectify some CSRF vulnerabilties in an application. However after configuring as specified here I am now getting the below messages in the log, when I navigate the application:
WARNING: potential cross-site request forgery (CSRF) attack thwarted (user:<anonymous>, ip:169.xx.x.xxx, uri:/myapp/MyAction, error:request token does not match session token)
Through including the:
<script src="/sui/JavaScriptServlet"></script>
On my main.jsp page the links have all been built incorporating the CSRFGuard token, e.g.
......./myapp/MyAction?CSRFTOKEN=BNY8-3H84-6SRR-RJXM-KMCH-KLLD-1W45-M18N
So I am unable to understand what I'm doing wrong that could cause the links to pass a token other than the expected value.
Please let me know if any additional information would make it easier to understand.
In case anyone stumbles across a similar issue:
Turned out that accessing the app using IE wasn't passing a token to an AJAX call, this would in turn result in the tokens being refreshed but the links in the already rendered page remained, causing the mismatch when clicked.
Found out the issue by building CSRFGuard myself from source and adding extra logging.
The primefaces commandlink and commandbutton seem to cause the csrfguard javascript to malfunction, if you have use these two component with ajax set to true (which is the default), it can prevent the token being injected after the ajax call
One of the possible fixes is to change the following 2 lines in Owasp.CsrfGuard.js file.
Change
function injectTokenForm(form, tokenName, tokenValue, pageTokens) {
var action = form.attribute("action");
To
function injectTokenForm(form, tokenName, tokenValue, pageTokens) {
var action = form.attributes["action"].value;
AND
Change
function injectTokenAttribute(element, attr, tokenName, tokenValue, pageTokens) {
location = element.getAttribute(attr);
To
function injectTokenAttribute(element, attr, tokenName, tokenValue, pageTokens) {
var location = null;
if (attr == "action") {
location = element.attributes[attr].value;
} else {
location = element.getAttribute(attr);
}

OAuth & facebook access_token not working, need OAuth expert

I have read the 1000+ blogs about how the redirect_uri has to be the same in both calls to OAuth in order to get a user token, but 100% of the time, regardless of how I format the URL, it fails with:
{
"error": {
"message": "Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request",
"type": "OAuthException",
"code": 100
}
}
I have been meticulous in making sure that the URLs in both calls were the exact same. My URL has to have a ? in it and I have tried replacing it with %3f but that didn't help. There has to be something else that can cause this error, I need to learn what that might be?
This seemed to break for me over the past month sometime. We did a show in late July and things worked fine (had a different base URL for that show since it was a different server). Could it be that the URL is of this format:
someprestuff.morestuff.mainurl.com?prm=value
Are there too many "parts" to the URL for Facebook to accept it?
I'm looking for alternate things to look for.
The url should be the same and it has to be escaped. In the url it has to look like this:
http%3A//someprestuff.morestuff.mainurl.com%3Fprm%3Dvalue
Jim's comment above worked, but to clarify, it was a forward slash that fixed it for us.
Had the same problem today, the problem turned out to be that the redirect_uri used a http:// URL Schema, and Facebook only accepts https://
I just finished tracing this issue on a server that was behind a load balancer. It turns out that while the load balancer was passing the HTTP_X_FORWARDED_PROTO header, somehow between the V3 PHP Facebook library, PHP and Apache, the header was not being recognized.
Here is the relevant code from the library:
protected function getHttpProtocol() {
if ($this->trustForwarded && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
return 'https';
}
return 'http';
}
/*apache + variants specific way of checking for https*/
if (isset($_SERVER['HTTPS']) &&
($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1)) {
return 'https';
}
/*nginx way of checking for https*/
if (isset($_SERVER['SERVER_PORT']) &&
($_SERVER['SERVER_PORT'] === '443')) {
return 'https';
}
return 'http';
}
As you can see, there are a few scenarios being accommodated here, so you'll have to ensure that whatever situation applies to you is properly configured for this block of code to succeed.
The most likely solution will be to set trustForwarded to true in your facebook config array.

Joomla JError doesn't show but then appears

I have 3d party component which set JError warning
JError::raiseWarning( 99, "Set your name please" );
$app = JFactory::getApplication();
$app->redirect($r);
Redirect goes to controller with code
function saveUserDetails(){
//some code here
//now I try to get that error which was set by raiseWarning
$other_errors = JError::getErrors();
print_r($other_errors);
die;
It returns just empty array. Why It doesn't contain that error?
Ok, I try to check session var with Joomla messages
$session =& JFactory::getSession();
$mes = $session->get('application.queue');
print_r($mes);
die;
Again empty. Where is that error, I can't understand.
If there is new request immediately after the redirect you might be loosing the session variable (JError content) inspect the fired requests with FireBug, Net tab, and see what happens there. Post any information you get there but if it's not in JErrors it shouldn't show on the site.
Can you give a link to the live site so I can test there and see the HTTP requests that could help.

How to use Zend_Form_Element_Hash?

Then I'm trying to use Zend_Form_Element_Hash it regenerates a hash every request.
In my code:
// form
$this->addElement('hash', 'hihacker', array('salt' => 'thesal'));
Then I dumping $_SESSION I see a new value each page reload.
Then I send a form it reports an error "The token '28a5e0e2a50a3d4afaa654468fd29420' does not match the given token 'a64407cc11376dac1916d2101de90d29'", each time - new pair of tokens
$form = new Form();
$form->addElement('hash', 'hihacker',
array('salt' => 'YOUR TOO MUCH SALTY TEXT !!##'));
if ($this->_request->isPost() && $form->isValid($this->_request->getPost())) {
// Valid ! you are safe do what ever you want .
} else if (count($form->getErrors('request_token')) > 0) {
///get him to the error controller
$this->_forward('csrf-forbidden', 'error');
return;
}
its working very well for me but double check your session setting
"
Internally, the element stores a unique identifier using Zend_Session_Namespace, and checks for it at submission (checking that the TTL has not expired). The 'Identical' validator is then used to ensure the submitted hash matches the stored hash.
The 'formHidden' view helper is used to render the element in the form.
"
form ZF docs
Zend_Form_Element_Hash is supposed to regenerate every request. What you're describing is your tokens going out of synch. This generally happens with multiple forms or with redirects/forwards.
If you're using ajax somewhere on the page you can put this in the controller action (near the end)
$form->hash->initCsrfToken();
$this->view->hash = $form->hash->getValue();
Then when you do the ajax call, just pull the token and replace the token on the form using a selector and .replaceWith(). This is how you deal with multiple forms as well
Otherwise you're probably either redirecting something or loading something twice and you should change the hop in the Zend library. The hop is how many times a token can be requested before it expires
Check that there is not a hidden redirect or forward somewhere in your script... the hash has a hop count of 1 so any redirect will make it expire.
FWIW i think there was a subtle bug in the hash a few versions of ZF ago. I got stuck on exactly the same problem, and hacked the code to make the hop count = 2. When I upgraded ZF this problem went away.