Facebook SDK返回错误:跨站请求伪造validation失败。 来自URL和会话的“状态”参数不匹配

我试图让Facebook的用户名使用这样的PHP SDK

$fb = new Facebook\Facebook([ 'app_id' => '11111111111', 'app_secret' => '1111222211111112222', 'default_graph_version' => 'v2.4', ]); $helper = $fb->getRedirectLoginHelper(); $permissions = ['public_profile','email']; // Optional permissions $loginUrl = $helper->getLoginUrl('http://MyWebSite', $permissions); echo '<a href="' . $loginUrl . '">Log in with Facebook!</a>'; try { $accessToken = $helper->getAccessToken(); var_dump($accessToken); } catch (Facebook\Exceptions\FacebookResponseException $e) { // When Graph returns an error echo 'Graph returned an error: ' . $e->getMessage(); exit; } catch (Facebook\Exceptions\FacebookSDKException $e) { // When validation fails or other local issues echo 'Facebook SDK returned an error: ' . $e->getMessage(); exit; } if (!isset($accessToken)) { if ($helper->getError()) { header('HTTP/1.0 401 Unauthorized'); echo "Error: " . $helper->getError() . "\n"; echo "Error Code: " . $helper->getErrorCode() . "\n"; echo "Error Reason: " . $helper->getErrorReason() . "\n"; echo "Error Description: " . $helper->getErrorDescription() . "\n"; } else { header('HTTP/1.0 400 Bad Request'); echo 'Bad request'; } exit; } // Logged in echo '<h3>Access Token</h3>'; var_dump($accessToken->getValue()); // The OAuth 2.0 client handler helps us manage access tokens $oAuth2Client = $fb->getOAuth2Client(); // Get the access token metadata from /debug_token $tokenMetadata = $oAuth2Client->debugToken($accessToken); echo '<h3>Metadata</h3>'; var_dump($tokenMetadata); // Validation (these will throw FacebookSDKException's when they fail) $tokenMetadata->validateAppId($config['11111111111']); // If you know the user ID this access token belongs to, you can validate it here //$tokenMetadata->validateUserId('123'); $tokenMetadata->validateExpiration(); if (!$accessToken->isLongLived()) { // Exchanges a short-lived access token for a long-lived one try { $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken); } catch (Facebook\Exceptions\FacebookSDKException $e) { echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n"; exit; } echo '<h3>Long-lived</h3>'; var_dump($accessToken->getValue()); } $_SESSION['fb_access_token'] = (string)$accessToken; 

但它给我这个错误:

 Facebook SDK returned an error: Cross-site request forgery validation failed. The "state" param from the URL and session do not match. 

请任何帮助我是新的PHP和Facebook SDK的感谢提前的任何帮助。

我发现,只要我在生成loginURL之前启用了PHP会话,并且在Facebook最终redirect到的脚本的顶部,它就可以自行工作而无需设置cookie( 根据ale500的答案 )。 这是使用sdk的5.1版本。

在这两个脚本的顶部,我加了…

 if(!session_id()) { session_start(); } 

…“只是工作”。

这是一个准系统完整的例子,为我工作:

auth.php

 if (!session_id()) { session_start(); } $oFB = new Facebook\Facebook([ 'app_id' => FACEBOOK_APP_ID, 'app_secret' => FACEBOOK_APP_SECRET ]); $oHelper = self::$oFB->getRedirectLoginHelper(); $sURL = $oHelper->getLoginUrl(FACEBOOK_AUTH_CALLBACK, FACEBOOK_PERMISSIONS); // Redirect or show link to user. 

auth_callback.php

 if (!session_id()) { session_start(); } $oFB = new Facebook\Facebook([ 'app_id' => FACEBOOK_APP_ID, 'app_secret' => FACEBOOK_APP_SECRET ]); $oHelper = self::$oFB->getRedirectLoginHelper(); $oAccessToken = $oHelper->getAccessToken(); if ($oAccessToken !== null) { $oResponse = self::$oFB->get('/me?fields=id,name,email', $oAccessToken); print_r($oResponse->getGraphUser()); } 

为什么?

作为补充说明,这在“回购协议”的文档中进行了解释。 看看这个页面上的警告。

警告:FacebookRedirectLoginHelper利用会话来存储CSRF值。 在调用getLoginUrl()方法之前,您需要确保已启用会话。 这通常在大多数Web框架中自动完成,但是如果您不使用Web框架,则可以添加session_start(); 到login.php&login-callback.php脚本的顶部。 您可以覆盖默认的会话处理 – 请参阅下面的扩展点。

我正在添加这个注释,因为如果你碰巧正在运行你自己的会话pipe理,或者你正在并行运行多个Web服务器,记住这一点很重要。 在这种情况下,依靠php的默认会话方法并不总是可行的。

在$ helper = $ fb-> getRedirectLoginHelper()后插入这段代码。

  $_SESSION['FBRLH_state']=$_GET['state']; 

它将工作或更多的细节访问Facebooklogin应用程序

已经提到的很多很好的答案,这里是帮助我的那个,

我发现问题是跨站请求伪造validation失败。 在FB代码中缺less所需的参数“状态” ,这里是解决scheme

在这一行之后

 $helper = $fb->getRedirectLoginHelper(); 

添加下面的代码,

 if (isset($_GET['state'])) { $helper->getPersistentDataHandler()->set('state', $_GET['state']); } 

我在使用Symfony2中的Facebook SDK的时候出现了这个错误,编写了一个Twig Extension来显示模板中API的数据。

对我来说,解决scheme是将'persistent_data_handler'=>'session'到Facebook对象configuration,这导致状态数据存储在会话密钥而不是内存中:

 $fb = new Facebook\Facebook([ 'app_id' => 'APP_ID', 'app_secret' => 'APP_SECRET', 'default_graph_version' => 'v2.4', 'persistent_data_handler'=>'session' ]); 

默认情况下,它使用内置的内存处理程序,这对我来说是不正确的。 也许是因为某些函数是在Twig扩展中被调用的,因为当在正常的控制器/服务中使用SDK时,内存处理程序可以正常工作。

显然,调用getLoginUrl()时会设置状态,并在您调用getAccessToken()检索状态。 如果保存的状态返回null (因为您的数据处理程序不像应该那样持久 ),则CSRFvalidation检查将失败。

如果您需要以特定的方式处理会话,或者希望将状态存储在其他位置,则还可以使用FacebookSessionPersistentDataHandler作为示例,使用'persistent_data_handler' => new MyPersistentDataHandler()编写自己的处理程序。

最后,看着FB代码,我发现这个问题

跨站请求伪造validation失败。 必需的参数“状态”丢失

$_SESSION['FBRLH_state']是由PHPvariables$_SESSION['FBRLH_state']造成的,当FB调用login-callback文件的时候出现一些“奇怪的”原因。

为了解决这个问题,我在函数$helper->getLoginUrl(...) "FBRLH_state"存储这个variables"FBRLH_state" 。 这个函数的调用是非常重要的,因为在这个函数里面当variables$_SESSION['FBRLH_state']被填充。

在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); 

在调用所有FB代码之前,在login-callback.php中:

 foreach ($_COOKIE as $k=>$v) { if(strpos($k, "FBRLH_")!==FALSE) { $_SESSION[$k]=$v; } } 

最后但并非最不重要的一点是,还要记住包含PHP会话的代码。

 if(!session_id()) { session_start(); } ... ... ... ... <?php session_write_close() ?> 

希望这个回应能帮助你节省8-10个小时的工作:) Bye,Alex。

发生这种情况时,Facebook库无法匹配它从Facebook接收的状态参数,默认情况下在会话中设置。 如果您正在使用像Laravel,Yii2或Kohana这样的框架来实现自己的会话存储,那么标准的Facebook会话实现将可能不起作用。

为了解决这个问题,你需要使用你的框架的会话库来创build你自己的PersistentDataInterface实现,并将其传递给Facebook\Facebook构造函数。

以下是来自Facebook的Laravel持久化处理程序示例:

 use Facebook\PersistentData\PersistentDataInterface; class MyLaravelPersistentDataHandler implements PersistentDataInterface { /** * @var string Prefix to use for session variables. */ protected $sessionPrefix = 'FBRLH_'; /** * @inheritdoc */ public function get($key) { return \Session::get($this->sessionPrefix . $key); } /** * @inheritdoc */ public function set($key, $value) { \Session::put($this->sessionPrefix . $key, $value); } } 

示例构造器参数:

 $fb = new Facebook\Facebook([ // . . . 'persistent_data_handler' => new MyLaravelPersistentDataHandler(), // . . . ]); 

更多信息在这里: https : //developers.facebook.com/docs/php/PersistentDataInterface/5.0.0

对我来说,问题是我没有在脚本之前运行一个会话。

所以,我添加了session_start(); 在实例化Facebook类之前。

同样的问题发生在我laravel 5.4我解决了这个问题的放置

 session_start(); 

在脚本的顶部。

下面是示例laravel控制器命名空间给你一个例子如何工作。

 <?php namespace App\Http\Controllers; session_start(); use Facebook\Facebook as Facebook; ?> 

该问题正在发生,因为尚未开始,所以通过在脚本的顶部添加会话开始,我们刚开始会话。

希望它可以帮助别人..

如果源主机名与经过身份validation的目标主机名不同,则会收到此错误。

 $loginUrl = $helper->getLoginUrl('http://MyWebSite', $permissions); 

有了这个声明,如果您的网站上的访问者使用http://www.mywebsite.com/将会引起跨站点错误。;

您必须确保源和目标主机名完全相同,包括最终的www前缀。

修正版本:

 $loginUrl = $helper->getLoginUrl('http://'.$_SERVER['SERVER_NAME'], $permissions); 

Facebook对象具有一个名为persistentDataHandler的实例variables,通常是FacebookSessionPersistentDataHandler一个实例,它具有访问本机PHP会话的set和get方法。

当使用以下命令生成callbackurl时:

 $loginUrl = $helper->getLoginUrl($callback_url, $permissions); 

方法FacebookRedirectLoginHelper->getLoginUrl()将创build一个32字符的随机string,将其添加到$loginUrl并使用persistentDataHandler的set方法将其保存到下面的代码中。

 $_SESSION['FBRLH_' . 'state'] 

后来当$helper->getAccessToken(); 被调用时,url中的状态参数将被存储在相同的会话中以防止CSRF。 如果不匹配,则会抛出exception。

FacebookSDKException跨站请求伪造validation失败。 必需的参数“状态”丢失。

所有这些说,你需要确保你的本地PHP会话function在这个过程中正确设置。 您可以通过添加来检查它

 die($_SESSION['FBRLH_' . 'state']); 

 $loginUrl = $helper->getLoginUrl($callback_url, $permissions); 

和之前

 $accessToken = $helper->getAccessToken(); 

看看是否有32个字符的string。

希望能帮助到你!

你可以做这个设置与新的状态会话

 <?php if(isset($_GET['state'])) { if($_SESSION['FBRLH_' . 'state']) { $_SESSION['FBRLH_' . 'state'] = $_GET['state']; } } ?> 

使用Symfony,它不会工作,因为会话的pipe理方式。

要解决这个问题,你可以创build一个与symfony会话一起工作的新处理程序。

FacebookDataHandlerSymfony.php:

 <?php use Facebook\PersistentData\PersistentDataInterface; use Symfony\Component\HttpFoundation\Session\Session; class FacebookDataHandlerSymfony implements PersistentDataInterface { private $session; public function __construct() { $this->session = new Session(); } public function get($key) { return $this->session->get('FBRLH_' . $key); } public function set($key, $value) { $this->session->set('FBRLH_' . $key, $value); } } 

而当你创buildFB对象时,你只需要指定新的类:

 $this->fb = new Facebook([ 'app_id' => '1234', 'app_secret' => '1324', 'default_graph_version' => 'v2.8', 'persistent_data_handler' => new FacebookDataHandlerSymfony() ]); 

很容易修复我。 我变了:

 $loginUrl = $helper->getLoginUrl('http://www.MYWEBSITE.ca/index_callback.php', $permissions); 

至:

 $loginUrl = $helper->getLoginUrl('http://MYWEBSITE.ca/index_callback.php', $permissions); 

去除'www'解决了这个问题。

在我的情况下,我已经检查错误,发现错误,导致我解决执行代码:

 date_default_timezone_set('Europe/Istanbul'); 

在脚本之前。 像魅力一样工作。 对于你的位置检查: timezones.europe.php

如果源主机名与目标主机名不同(如Souch提到的),则触发该错误。 当访问者在URL地址框中键入与“ http://www.website.com ”不同的“ http://website.com ”时。 我们通过在session_start()之前的最上面的位置添加下面的代码来将它们redirect到正确的URL。

  if($_SERVER['HTTP_HOST']!=='www.website.com'){ header('location: http://www.website.com'); } 

Yii2解决scheme适用于我:

 use Facebook\PersistentData\PersistentDataInterface; use Yii; class PersistentDataHandler implements PersistentDataInterface { /** * @var string Prefix to use for session variables. */ protected $sessionPrefix = 'FBRLH_'; public function get($key) { return Yii::$app->session->get($this->sessionPrefix . $key); } public function set($key, $value) { Yii::$app->session->set($this->sessionPrefix . $key, $value); } } 

我有同样的错误,因为我忘了添加“www”。 发件人地址。 在客户端 – OAuth设置中必须有正确的名称。

这是FB Api面临的一个常见问题。 这只是一个SESSION问题。 要解决这个问题,请添加一些代码。

关于callback脚本通常fb-callback.php添加“session_start();” 就在你包含Facebook自动加载文件之前。 然后“$ _SESSION ['FBRLH_state'] = $ _ GET ['state'];” 之后的“$ helper = $ fb-> getRedirectLoginHelper();” 线。

例如:

 <?php session_start(); include 'vendor/autoload.php'; include 'config.php'; /*Facebook Config*/ $helper = $fb->getRedirectLoginHelper(); $_SESSION['FBRLH_state']=$_GET['state']; try { $accessToken = $helper->getAccessToken(); } ?> 

可能会帮助某人在前端使用Javascript Helper来validation用户身份,并且在PHP中尝试从redirectlogin助手中提取access_token 。 所以使用以下

 getJavaScriptHelper(); 

代替

 getRedirectLoginHelper(); 

间歇性问题的解决scheme

我是a)redirect到Facebooklogin链接,b)从login.phpredirect到main.php。 用户将前往main.php和其他几个页面,然后单击返回浏览器。

最终,他们会在login.php中发布一系列的声明,但Facebook在一次成功后删除了$ _SESSION ['FBRLH_state'],所以即使它具有正确的$ _GET ['state']出错。

解决方法是a)如果用户login,则在内部进行跟踪,并避免login.php中的重复Facebook逻辑,或者b)跟踪特定用户的所有最近有效的状态参数(可能在一个会话中) Facebook,如果$ _GET ['state']在该数组中,则执行以下操作:

$ _SESSION ['FBRLH_state'] = $ _GET ['state'];

在这种情况下,您可以安全地做到这一点,而不破坏CSRF保护。

这可能是比较晚,但我希望它可以帮助别人,因为这个问题仍然存在。

我有这个问题一段时间,我已经查了很多不同的解决scheme,其中许多禁用CSRF检查。 所以,在我阅读过的所有东西之后,这对我来说是有效的。

据我所知,当您的redirecturl与您在应用设置中设置的url不匹配时,会出现此错误,所以我的问题每次都很容易解决,但是我也看到人们由于没有正确启动会话而出现问题,所以我将涵盖这两个问题。

步骤1:确保您的会话在需要时开始。

例如:fb-config.php

 session_start(); include_once 'path/to/Facebook/autoload.php'; $fb = new \Facebook\Facebook([ 'app_id' => 'your_app_id', 'app_secret' => 'your_secret_app_id', 'default_graph_version' => 'v2.10' ]); $helper = $fb->getRedirectLoginHelper(); 

如果您的Facebookcallback代码位于除configuration文件之外的另一个文件中,则也要在该文件上启动会话。

例如:fb-callback.php

 session_start(); include_once 'path/to/fb-config.php'; try { $accessToken = $helper->getAccessToken(); } catch (\Facebook\Exceptions\FacebookResponseException $e) { echo "Response Exception: " . $e->getMessage(); exit(); } catch (\Facebook\Exceptions\FacebookSDKException $e) { echo "SDK Exception: " . $e->getMessage(); exit(); } /** THE REST OF YOUR CALLBACK CODE **/ 

现在,什么解决了我的实际问题。

第3步:在您的应用设置中设置您的redirecturl。

在您的Facebooklogin应用设置中,转到有效的OAuthredirectURI ,您应该添加指向您的fb-callback.php文件的url。

 http://example.com/fb-callback.php AND ALSO http://www.example.com/fb-callback.php 

然后设置您的redirecturl如下。

 $redirectURL = "http://".$_SERVER['SERVER_NAME']."/fb-callback.php"; $permissions = ['email']; $fLoginURL = $helper->getLoginUrl($redirectURL, $permissions); 

为什么使用和不使用www以及为什么使用SERVER_NAME?

因为您的有效OAuthredirectURI需要与代码中的redirecturl匹配,并且在您的应用设置中,您只需将OAuthredirect设置为http://example.com/fb-callback.php ,并将您的$ redirectURL设置为http: //example.com/fb-bacllback.php ,使其匹配,但用户input您的网站作为http://www.example.com然后用户将得到;Facebook SDK错误:跨站请求伪造validation失败。 必需的参数“状态”从持久性数据中丢失,因为用户所在的URL并不完全符合您所设置的内容。 为什么? 我没有任何想法。

我的方法使得如果用户以http://example.com或http://www.example.com进入您的网站,它将始终与您在应用设置中设置的内容相匹配。; 为什么? 因为根据用户在浏览器中inputurl的方式,$ _SERVER ['SERVER_NAME']将返回带有或不带www的域。

这是我的发现,这是唯一的工作,我没有删除CSRF检查,迄今没有问题。

我希望这有帮助。

对我来说,设置会话状态起作用

完整的代码(在redirecturl php中)

 $accessToken = ''; $helper = $fb->getRedirectLoginHelper(); if(isset($_GET['state'])){ $_SESSION['FBRLH_state']=$_GET['state']; } try { $accessToken = $helper->getAccessToken(); } catch ( Facebook\Exceptions\FacebookResponseException $e ) { // When Graph returns an error echo 'Graph returned an error: ' . $e->getMessage(); } 

在所有页面中查看你的“default_graph_version”,如果改变,那么保持它们正确显示在https://developers.facebook.com/apps/