Facebook SDK返回错误:跨站点请求伪造验证失败.来自URL和会话的"状态"参数不匹配

Fad*_*adi 57 php wordpress facebook facebook-graph-api

我正在尝试使用像这样的php sdk获取Facebook用户ID

$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;
Run Code Online (Sandbox Code Playgroud)

但它给了我这个错误:

Facebook SDK returned an error: 
Cross-site request forgery validation failed. 
The "state" param from the URL and session do not match.
Run Code Online (Sandbox Code Playgroud)

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

eno*_*rev 99

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

在两个脚本的顶部,我添加了......

if(!session_id()) {
    session_start();
}
Run Code Online (Sandbox Code Playgroud)

......它"刚刚奏效".

这是一个适合我的准系统完整示例:

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.
Run Code Online (Sandbox Code Playgroud)

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());
}
Run Code Online (Sandbox Code Playgroud)

为什么?

作为补充说明,这在回购文件中有解释.请查看此页面上的警告.

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

我正在添加此注释,因为如果您正在运行自己的会话管理或者并行运行多个Web服务器,请务必记住这一点.在这些情况下,依赖php的默认会话方法并不总是有效.


小智 64

在$ helper = $ fb-> getRedirectLoginHelper()之后插入此代码;

  $_SESSION['FBRLH_state']=$_GET['state'];
Run Code Online (Sandbox Code Playgroud)

它将工作或更多细节访问Facebook登录应用程序

  • 应该注意,这会禁用SDK提供的CSRF保护. (9认同)
  • 你不应该这样做 (2认同)

G.A*_*mar 49

已经提到了很多很棒的答案,这里有一个对我有帮助的答案,

我发现问题是跨站点请求伪造验证失败.FB代码中缺少必需的参数"状态",这是解决方案

这一行之后

$helper = $fb->getRedirectLoginHelper();
Run Code Online (Sandbox Code Playgroud)

添加以下代码,

if (isset($_GET['state'])) {
    $helper->getPersistentDataHandler()->set('state', $_GET['state']);
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,此类解决方案会破坏CSRF保护.因为保护基于存储令牌和收到的令牌的比较.您的代码强制它们匹配 - 您覆盖存储的已接收. (7认同)

okd*_*wit 22

我在Symfony2中使用Facebook SDK时出现此错误,编写Twig扩展以在模板中显示来自API的数据.

我的解决方案是添加'persistent_data_handler'=>'session'到Facebook对象配置,这会导致状态数据存储在会话密钥而不是内存中:

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

默认情况下,它使用的内置内存处理程序对我来说无法正常工作.也许是因为某些函数是在Twig扩展中调用的,因为内存处理程序在普通控制器/服务中专门使用SDK时可以正常工作.

显然,当你打电话时getLoginUrl(),状态就会被设定,并且在你打电话的时候被检索getAccessToken().如果保存的状态返回null(因为您的数据处理程序不像应该的那样持久),则CSRF验证检查失败.

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


Geo*_*rge 16

当Facebook库无法将其从Facebook收到的状态参数与默认情况下在会话中设置的状态参数匹配时,就会发生这种情况.如果您使用的是Laravel,Yii2或Kohana等框架来实现自己的会话存储,那么标准的Facebook会话实现可能无法正常工作.

要修复它,您需要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);
  }
}
Run Code Online (Sandbox Code Playgroud)

示例构造函数参数:

$fb = new Facebook\Facebook([
  // . . .
  'persistent_data_handler' => new MyLaravelPersistentDataHandler(),
  // . . .
 ]);
Run Code Online (Sandbox Code Playgroud)

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


小智 8

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

跨站点请求伪造验证失败.必需的参数"状态"缺失

和similars是由PHP变量引起的,$_SESSION['FBRLH_state']当FB调用login-callback文件时出于某种"奇怪"的原因.

为了解决这个问题,我"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);
Run Code Online (Sandbox Code Playgroud)

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

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

最后,但并非最不重要的是,还记得包含PHP会话的代码,所以..

if(!session_id()) {
    session_start();
}
...
...
...
...
<?php session_write_close() ?>
Run Code Online (Sandbox Code Playgroud)

我希望这个回复可以帮助你节省8-10个小时的工作:)再见,亚历克斯.


Nin*_*pac 7

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

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