Facebook PHP SDK 5:: API 2.4::跨站请求伪造验证失败.所需参数"状态"失


Facebook PHP SDK 5 :: API 2.4 :: Cross-site request forgery validation failed. Required param "state" missing

我做了一个非常简单的PHP脚本,只是为了尝试通过Facebook登录并获得accessToken。但是当我尝试以下代码时,我从SDK得到一个异常:«跨站点请求伪造验证失败。缺少必需的参数"state"。»。

下面是我的代码:
require_once __DIR__ . '/facebook-sdk-v5/autoload.php';
session_start();
$fb = new Facebook'Facebook([
    'app_id' => '{my-own-app-id}',
    'app_secret' => '{my-own-app-secret}'
]);
// Check to see if we already have an accessToken ?
if (isset($_SESSION['facebook_access_token'] )) {
    $accessToken = $_SESSION['facebook_access_token'];
    echo "Horray we have our accessToken:$accessToken<br />'n";
} else {
    // We don't have the accessToken
    // But are we in the process of getting it ? 
    if (isset($_REQUEST['code'])) {
        $helper = $fb->getRedirectLoginHelper();
        try {
            $accessToken = $helper->getAccessToken();
            } 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)) {
              // Logged in!
              $_SESSION['facebook_access_token'] = (string) $accessToken;
              // Now you can redirect to another page and use the
              // access token from $_SESSION['facebook_access_token']
              echo "Finally logged in! Token:$accessToken";
        }           
    } else {
        // Well looks like we are a fresh dude, login to Facebook!
        $helper = $fb->getRedirectLoginHelper();
        $permissions = ['email', 'user_likes']; // optional
        $loginUrl = $helper->getLoginUrl('http://mywebsite.com/myapp/index.php', $permissions);
        echo '<a href="' . $loginUrl . '">Log in with Facebook!</a>';
    }
}
exit;

我不得不在一些服务器中添加这些行:

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

我随机得到这个错误,取决于服务器配置。

session_start()在两个脚本的开头。我从这里得到了解决方案:https://github.com/facebook/facebook-php-sdk-v4/issues/473

如果你使用一个"www"版本的网站来生成登录链接,你得到重定向到非www版本,你会遇到你的会话问题。因此,请确保您访问网站的www版本,然后将回调url定义为相同的www版本。此外,您可以在服务器配置中定义永久重定向,以确保从非www版本访问您网站的任何人都被重定向到www版本,反之亦然。

我也遇到了同样的问题,在研究了stackoverflow之后放行

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

以上已经解决了我的问题

$helper = $fb->getRedirectLoginHelper();  

如果您的原始主机名与经过身份验证的目标主机名不同,则会收到此错误。

$loginUrl = $helper->getLoginUrl('http://mywebsite.com/myapp/index.php', $permissions);

使用此语句,如果您网站上的访问者使用http://www.mywebsite.com/,则会引发跨站点错误。

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

固定版本:

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

Laravel 5.2

我也有这个错误"跨站点请求伪造验证失败。必需参数" state " missing .

读了几个小时之后。我试图更改供应商脚本。

vendor'facebook'php-sdk-v4'src'Facebook'Helpers'FacebookRedirectLoginHelper.php

在第123行,我修改了这个脚本:

private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
{
    $state = $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
    $this->persistentDataHandler->set('state', $state);
    return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
}

into (I add Session::put('state', $state);)

private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
{
    $state = $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
    $this->persistentDataHandler->set('state', $state);
    Session::put('state', $state);
    return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
}
在第234行,我修改了这个脚本:
protected function validateCsrf()
{
    $state = $this->getState();
    $savedState = $this->persistentDataHandler->get('state');
    if (!$state || !$savedState) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. Required param "state" missing.');
    }
    $savedLen = strlen($savedState);
    $givenLen = strlen($state);
    if ($savedLen !== $givenLen) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
    $result = 0;
    for ($i = 0; $i < $savedLen; $i++) {
        $result |= ord($state[$i]) ^ ord($savedState[$i]);
    }
    if ($result !== 0) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
}

into (I added $this->persistentDataHandler->set('state', Session::get('state'));)

protected function validateCsrf()
{
    $state = $this->getState();
    $this->persistentDataHandler->set('state', Session::get('state'));
    $savedState = $this->persistentDataHandler->get('state');
    if (!$state || !$savedState) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. Required param "state" missing.');
    }
    $savedLen = strlen($savedState);
    $givenLen = strlen($state);
    if ($savedLen !== $givenLen) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
    $result = 0;
    for ($i = 0; $i < $savedLen; $i++) {
        $result |= ord($state[$i]) ^ ord($savedState[$i]);
    }
    if ($result !== 0) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
}

这就是我所做的。错误消失了。

在获得令牌之前显式设置[PersistentDataHandler]将保证请求成功。下面展示了如何获取$_GET['state'] &只需将其注入Symfony 2上的"要使用的[助手]"。3.x

我遇到了与op完全相同的问题,这解决了我的问题。

//create your new facebook sdk instance
$this->fb = new Facebook([
   'app_id' => $facebookAppId,
   'app_secret' =>$facebookAppSecret,
   'default_graph_version' =>$facebookDefaultGraphVersion
]);    
//retrieve the helper
$this->helper = $this->fb->getRedirectLoginHelper();
//below is the money shot
//setting this explicitly before you get your tokens will guarantee that the request will be a success. It Fetches $_GET['state'] & simply injects it into the "to be used [helper]"
$this->helper->getPersistentDataHandler()->set('state', $request->query->get('state'));

这是许多人在FB Api中面临的一个常见问题。这只是一个SESSION问题。要解决这个问题,可以添加一些代码,如。

在回调脚本通常fb-callback.php添加"session_start();"就在你包含facebook自动加载文件之前。然后在"$helper =$ fb->getRedirectLoginHelper();"行之后输入"$_SESSION['FBRLH_state']=$_GET['state'];"。

示例:

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

真正的问题是我的PHP文件的编码。我使用UTF8,但它应该是UTF8 w/o BOM。

错误编码的副作用是我的SESSION不能正常工作,然后SDK无法检索正常工作所需的信息。

一个正确配置的错误报告应该直接告诉有一个问题。

我想我们可以把这个bug归为"新手"一类。但是,我仍然认为它对像我这样的新手很有用。

对于我来说,现在只需通过添加以下内容来启动会话即可解决问题:

session_start();

在两个文件的开头(生成facebook url的第一个文件和回调文件:login.php和fb-callback.php (https://developers.facebook.com/docs/php/howto/example_facebook_login))。

我还得加上这个:

$config['app_id'] = 'myapp_id';

在顶部,以防止另一个不相关的错误。

正如我在这里回答的:Facebook SDK返回错误:跨站点请求伪造验证失败。"state"URL和会话的参数不匹配

您需要确保本机PHP会话特性已正确设置。

对于我来说,这是由于'persistent_data_handler'而发生的。通过在Facebook配置中添加这个,我能够使它工作。

session_start();
$fb = new Facebook'Facebook([
  'app_id'     => '6XXXXXXXXX',
  'app_secret' => '5XXXXXXXXXXXXXX',
  'default_graph_version' => 'v2.6', 
  'persistent_data_handler' => 'session'
  ]);

我遇到了类似的问题,找到了Nanang Koesharwanto的解决方案。这更像是在供应商目录中修改源文件是一个非常坏主意。这就是诀窍。

public function callback(Request $request)
{
    $this->helper->getPersistentDataHandler()->set('state', $request->state);
    return $this->helper->getAccessToken();
}

如果失败,将use Session;放入控制器

我试图在Facebook PHP SDK的Symfony中实现Facebook登录,我有相同的错误"跨站点请求伪造验证失败。必需参数" state "缺失"

我通过将persistent_data_handler参数添加到我的facebook应用实例化中,并使用自定义处理程序实现facebook PHP SDK的PersistentDataInterface来解决这个问题。

妙不可言

public function facebookCallbackAction(Request $request) {
    $session = $request->getSession();
    $fb = new 'Facebook'Facebook([
        'app_id' => $this->container->getParameter('facebook_app_id'),
        'app_secret' => $this->container->getParameter('facebook_app_secret'),
        'default_graph_version' => 'v2.5',
        'persistent_data_handler' => new SymfonyPersistentDataHandler($session),
    ]);
}

我的自定义处理器:

use Facebook'PersistentData'PersistentDataInterface;
use Symfony'Component'HttpFoundation'Session'Session;
class SymfonyPersistentDataHandler implements PersistentDataInterface {
    protected $session;
    protected $sessionPrefix = 'FBRLH_';
    public function __construct(Session $session) {
        $this->session = $session;
    }
    public function get($key) {
        return $this->session->get($this->sessionPrefix . $key);
    }
    public function set($key, $value) {
        $this->session->set($this->sessionPrefix . $key, $value);
    }
}

最后,查看FB代码,我发现问题"跨站点请求伪造验证失败。当FB调用登录回调文件时,由于一些"奇怪"的原因,PHP变量$_SESSION['FBRLH_state']导致必需的参数" state "缺失"和类似的情况。

要解决这个问题,我存储这个变量"FBRLH_state"后函数$helper->getLoginUrl(…)的调用。这是非常重要的,只有在调用这个函数之后,因为当变量$_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);

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

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个小时的工作:)再见,亚历克斯。

您必须确保会话在脚本运行之前启动。但是如果你在同一个脚本上再次启动会话,它会抛出443: Network is unreachable。希望这能帮助到一些人。

我刚用过if (session_status() == PHP_SESSION_NONE){ session_start(); }

对于使用cakephp 3的人。X和这个问题,你不知道怎么解它。添加session_start ();在你的验证和回调方法的开始。

public function Facebookauth()
{
session_start();
 $fb = new Facebook([
      'app_id' => '{app_id}',
      'app_secret' => '{app_secret}',
      'default_graph_version' => 'v2.6',
     ..........
    ]);
you can still use 
     $session = $this->request->session();

我的修复是在config/session.php中将'secure' => true更改为false。我不小心将此设置为true,而不使用https放在首位。

对于我来说,解决方案是通过将命名空间Facebook'Exceptions'FacebookSDKException替换为Exception来捕获异常,因为脚本已经使用了它。

use Facebook'Facebook;
// code ...
$fb = new Facebook([
    'app_id' => FB_APP_ID,
    'app_secret' => FB_APP_SECRET,
    'default_graph_version' => 'v2.5',
]);
$helper = $fb->getRedirectLoginHelper();
try {
    $accessToken = $helper->getAccessToken();
} catch (Exception $e) {
    echo $e->getMessage();
    exit;
}

如果session_start()仍然没有解决问题,你在你的Facebook开发者应用程序的有效OAuth重定向URI中放了一个错误的URI。

来解决:首先,转到fb-callback.php,找到您放在header函数中的URI,例如:header("Location: https://localhost/home.php")。然后转到你的Facebook开发者应用程序,然后在侧边栏中,点击产品选项卡下的Facebook登录,然后点击设置。在Valid OAuth Redirect URI中,添加您放在header中的URI。在我的例子中,我将输入https://localhost/home.php。

希望这对你有帮助。

我解决了这个错误通过设置我的回叫url在"Facebook ->登录->有效的OAuth重定向uri"在Facebook的开发者帐户。

最后,我用另一种方法解决了我所有的问题,facebook开发者文档中的例子已经过时了。

SDK V5和APi 2.4作品类似于本教程中描述的,需要定义访问令牌

请确保您在APP-IP|APP-SECRET中填写了您的凭据

教程中的例子:

$fb = new Facebook'Facebook([
      'app_id' => 'APP-ID',
      'app_secret' => 'APP-SECRET',
      'default_graph_version' => 'v2.4',
      'default_access_token' => 'APP-ID|APP-SECRET'
]);

使用这个,而不是开发者facebook文档中的例子。

玩得开心点:)

无论你做什么。只是不要多次调用getAccessToken()。一旦会话被调用,它就会从会话中删除状态。

相关文章: