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/