Пакет SDK Facebook вернул ошибку: не удалось проверить подделку межсайтового запроса. Параметр "состояние" из 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 заранее благодарю за любую помощь.

Author: Fadi, 2015-08-15

24 answers

Я обнаружил, что до тех пор, пока я включал сеансы PHP перед созданием URL-адреса для входа, и в верхней части сценария, на который в конечном итоге перенаправляется Facebook, он отлично работает сам по себе, не устанавливая файл 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(). Обычно это делается автоматически в большинстве веб-фреймворков, но если вы не используете веб-фреймворк, который вы можете добавить session_start(); в верхней части вашего login.php & login-callback.php сценарии. Вы можете перезаписать обработку сеанса по умолчанию - см. Пункты расширения ниже.

Я добавляю это примечание, потому что важно иметь в виду, если вы используете собственное управление сеансами или если вы используете несколько веб-серверов параллельно. В этих случаях использование методов сеанса php по умолчанию не всегда будет работать.

 83
Author: enobrev, 2017-07-12 21:42:05

Вставьте этот код после $помощник = $fb->getredirectloginhelper();

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

И это будет работать или для получения более подробной информации посетите приложения для входа в facebook

 60
Author: zratan, 2016-06-08 04:13:11

Уже упоминалось множество замечательных ответов, вот тот, который помог мне,

Я обнаружил, что проблема заключается в том, что Не удалось проверить подделку межсайтового запроса. Требуемый параметр "состояние" отсутствует в коде FB, и вот решение

После этой строки

$helper = $fb->getRedirectLoginHelper();

Добавьте приведенный ниже код,

if (isset($_GET['state'])) {
    $helper->getPersistentDataHandler()->set('state', $_GET['state']);
}
 43
Author: G.Ashok Kumar, 2017-07-20 07:25:38

Я получил эту ошибку при использовании SDK Facebook в Symfony2, написав расширение 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'
]);

По умолчанию он использовал встроенный обработчик памяти, который не работал должным образом для меня. Может быть, потому, что некоторые функции вызываются из расширения ветки, как память обработчик работает при использовании SDK исключительно в обычных контроллерах/службах.

По-видимому, состояние устанавливается при вызове getLoginUrl() и извлекается в любое время, когда вы вызываете getAccessToken(). Если сохраненное состояние возвращает null (потому что ваш обработчик данных не такой постоянный , как должно быть), проверка CSRF завершается неудачей.

Если вам нужно обрабатывать сеансы определенным образом или вы хотите сохранить состояние где-то в другом месте, вы также можете написать свой собственный обработчик с помощью 'persistent_data_handler' => new MyPersistentDataHandler(), используя В качестве примера можно привести FacebookSessionPersistentDataHandler.

 20
Author: Fx32, 2016-01-01 15:44:13

Это происходит, когда библиотека Facebook не может сопоставить параметр состояния, который она получает от Facebook, с тем, который она устанавливает по умолчанию в сеансе. Если вы используете фреймворк, такой как Laravel, Yii2 или Kohana, который реализует собственное хранилище сеансов, стандартная реализация сеанса Facebook, скорее всего, не будет работать.

Чтобы исправить это, вам нужно создать свою собственную реализацию PersistentDataInterface, используя библиотеку сеансов вашей платформы, и передать ее в Facebook\Facebook конструктор.

Вот пример обработчика сохраняемости Laravel из Facebook:

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

 12
Author: George, 2016-07-08 04:59:44

Наконец, заглянув в код FB, я обнаружил, что проблема

Не удалось выполнить проверку подделки межсайтового запроса. Требуемый параметр "состояние" отсутствует

И подобные вызваны переменной PHP $_SESSION['FBRLH_state'], которая по какой-то "странной" причине, когда FB вызывает файл обратного вызова для входа в систему.

Для ее решения я сохраняю эту переменную "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 часов работы:) Пока, Алекс.

 8
Author: ale500, 2016-01-18 12:34:02

Для меня проблема заключалась в том, что я не запускал сеанс перед сценарием.

Итак, я добавил session_start(); перед созданием экземпляра класса Facebook.

 7
Author: Nino Škopac, 2016-12-19 21:55:13

Та же проблема возникла у меня на laravel 5.4. Я решил эту проблему, поставив

session_start();

В верхней части сценария.

Ниже приведен пример пространства имен контроллера laravel, чтобы показать вам пример того, как это будет работать.

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

Проблема возникает из-за того, что он еще не запущен, поэтому, добавив начало сеанса в верхней части сценария, мы только начинаем сеанс.

Надеюсь, это кому-нибудь поможет..

 4
Author: usama, 2017-06-25 07:42:12

Объект Facebook имеет переменную экземпляра с именем persistentDataHandler, обычно экземпляр FacebookSessionPersistentDataHandler, который имеет набор и метод get для доступа к собственным сеансам PHP.

При создании URL-адреса обратного вызова с использованием:

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

Метод FacebookRedirectLoginHelper->getLoginUrl() создаст случайную строку из 32 символов, добавит ее в $loginUrl и сохранит в приведенном ниже коде, используя метод набора persistentDataHandler.

$_SESSION['FBRLH_' . 'state']

Позже, когда будет вызван $helper->getAccessToken();, параметр состояния в URL-адресе будет сравниваться с сохраненным в тот же сеанс для предотвращения CSRF. Если не совпадает, будет выдано исключение.

FacebookSDKException: Не удалось выполнить проверку на подделку межсайтового запроса. Требуемый параметр "состояние" отсутствует.

Все это, как говорится, вам нужно убедиться, что ваша собственная функция сеанса PHP правильно настроена во время этого процесса. Вы можете проверить это, добавив

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

После

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

И до

$accessToken = $helper->getAccessToken();

Чтобы увидеть, есть ли эта 32-символьная строка.

Надеюсь, это поможет!

 3
Author: Chao Chen, 2016-01-22 08:34:07

Вы получаете эту ошибку, если исходное имя хоста отличается от имени целевого хоста после проверки подлинности.

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

С этим заявлением, если посетитель вашего веб-сайта использовал http://www.mywebsite.com / возникнет межсайтовая ошибка.

Вы должны убедиться, что исходное и целевое имя хоста точно совпадают, включая возможный префикс www.

Исправленная версия:

$loginUrl = $helper->getLoginUrl('http://'.$_SERVER['SERVER_NAME'], $permissions);
 2
Author: Jean.R, 2016-01-03 23:48:59

Вы могли бы просто сделать это, установив сеанс в новом состоянии

<?php
if(isset($_GET['state'])) {
      if($_SESSION['FBRLH_' . 'state']) {
          $_SESSION['FBRLH_' . 'state'] = $_GET['state'];
      }
}
?>
 2
Author: Joshua Bigboy Springman, 2016-06-15 19:35:08

С Symfony это не работает, потому что способ управления сеансом.

Для решения проблемы вы можете создать новый обработчик, который будет работать с сеансом 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);
    }

}

И когда вы создаете объект FB, вам нужно просто указать новый класс:

$this->fb = new Facebook([
            'app_id' => '1234',
            'app_secret' => '1324',
            'default_graph_version' => 'v2.8',
            'persistent_data_handler' => new FacebookDataHandlerSymfony()
        ]);
 2
Author: user4520510, 2016-10-15 23:41:56

Это может быть немного поздно, но я надеюсь, что это поможет другим, так как эта проблема все еще сохраняется.

У меня была эта проблема некоторое время, и я искал и видел множество различных решений, многие из которых отключают проверку CSRF. Так что после всего, что я прочитал, это то, что сработало для меня.

Насколько я понимаю, вы получаете эту ошибку, когда ваш 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();

Если ваш код обратного вызова facebook находится в другом файле, кроме конфигурации, то запустите сеанс и в этом файле.

Например: 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: Настройте свой перенаправление URL-адреса в настройках вашего приложения.

В настройках приложения для входа в Facebook перейдите к Действительным URI перенаправления OAuth, где вы должны были добавить URL-адрес, указывающий на ваш fb-callback.php файл.

http://example.com/fb-callback.php

AND ALSO

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

Затем настройте URL-адрес перенаправления следующим образом.

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

Почему как с www, так и без него и зачем использовать ИМЯ_СЕРВЕРА?

Потому что ваш Действительный URI перенаправления OAuth должен совпадать с вашим URL-адресом перенаправления в вашем коде, и если в настройках приложения вы задаете только Перенаправление OAuth как http://example.com/fb-callback.php и настройте свой $redirectUrl как http://example.com/fb-bacllback.php чтобы он соответствовал, но пользователь вошел на ваш сайт как http://www.example.com затем пользователь получит Ошибку Facebook SDK: проверка подделки межсайтового запроса не удалась. Требуемый параметр "состояние" отсутствует в постоянных данных, поскольку URL-адрес, по которому находится пользователь, не совсем соответствует тому, что у вас настроено. Почему? У меня нет никакого долбаного идея.

Мой подход делает это так, если пользователь заходит на ваш сайт как http://example.com или http://www.example.com , он всегда будет соответствовать тому, что вы настроили в настройках своего приложения. почему? потому что $_SERVER['ИМЯ_СЕРВЕРА'] вернет домен с www или без него в зависимости от того, как пользователь ввел URL-адрес в браузере.

Это мои выводы, и это единственное, что сработало для меня без удаления проверки CSRF, и до сих пор никаких проблем.

Я надеюсь, это поможет.

 2
Author: BlueSun3k1, 2017-11-05 18:57:06

Простое решение для меня. Я изменил:

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

Кому:

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

Удаление 'www' решило проблему.

 1
Author: Mathieu Wilson, 2016-01-27 21:23:06

В моем случае я проверил ошибку и нашел ошибку, которая привела меня к решению с помощью исполняемого кода:

date_default_timezone_set('Europe/Istanbul');

Перед сценарием. Сработало как заклинание. Для проверки вашего местоположения: timezones.europe.php

 1
Author: Roman Losev, 2017-03-28 17:27:09

Ошибка возникает, если исходное имя хоста отличается от имени целевого хоста [как упоминалось выше]. Когда посетители вводили в поле URL-адрес как "http://website.com "это отличается от "http://www.website.com ". Мы перенаправляем их на правильный URL-адрес, добавляя следующие коды в самой верхней позиции перед session_start().

  if($_SERVER['HTTP_HOST']!=='www.website.com'){
  header('location: http://www.website.com');
  }
 0
Author: Piya Poonsawat, 2016-02-23 03:59:53

Решение Yii2, которое работает для меня:

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);
    }
}
 0
Author: zibra, 2016-09-06 12:28:43

У меня была та же ошибка, потому что я забыл добавить "www." к адресу отправителя. В настройках Client-OAuth должно быть правильное имя.

 0
Author: Umbrella Brothers, 2017-04-09 16:39:23

Это распространенная проблема, с которой сталкиваются многие люди в Api FB. это всего лишь проблема СЕАНСА. Чтобы решить эту проблему, добавьте такой код, как.

В сценарии обратного вызова обычно fb-callback.php добавьте "session_start();" непосредственно перед включением файла автоматической загрузки facebook. и затем "$_SESSION['fbrlh_state']=$_GET['состояние'];" после строки "$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();
} ?>
 0
Author: Er Prabhjot Singh Tugger, 2017-05-30 07:02:21

Может помочь кому-то, кто использует Помощник Javascript в интерфейсе для аутентификации пользователя, а в PHP пытается извлечь access_token из Помощника по перенаправлению входа . Поэтому используйте следующие

getJavaScriptHelper();

Вместо

getRedirectLoginHelper();
 0
Author: makki, 2017-06-24 23:01:04

РЕШЕНИЕ ПЕРИОДИЧЕСКИХ ПРОБЛЕМ

Я был а) перенаправлением на ссылку для входа в Facebook, б) перенаправлением с login.php чтобы main.php . Пользователи будут путешествовать в main.php и несколько других страниц, затем нажмите "Вернуться назад" в браузере.

В конце концов, они ударят login.php с кучей размещенных на нем кредитов, но Facebook удаляет $_SESSION['fbrlh_state'] после одного успеха, поэтому, даже если у него был правильный $_GET['состояние'], он будет ошибочным.

Решение состоит в том, чтобы a) отслеживайте внутренне, вошел ли пользователь в систему, и избегайте повторения логики Facebook в login.php , ИЛИ б) отслеживать все недавно действительные параметры состояния для этого конкретного пользователя (возможно, в сеансе), которые были установлены Facebook, и если $_GET['состояние'] находится в этом массиве, то сделайте следующее:

$_SESSION['FBRLH_STATE']=$_GET['состояние'];

В этом случае вы можете сделать это безопасно, не нарушая защиту CSRF.

 0
Author: Phyllis Sutherland, 2017-09-29 15:21:25

Для меня настройка состояния сеанса сработала

Полный код (в 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();

}
 0
Author: digitalzoomstudio, 2017-11-13 19:12:09

Если вы продолжаете видеть ошибку, просто очистите кэш вашего браузера. Когда я исправил проблему с помощью решения enobrev, я продолжал испытывать ошибку. Через некоторое время я понял, что мне нужно очистить кэш, и после того, как я перезапустил браузер, он заработал!

 0
Author: fancoolo, 2018-08-19 16:02:17

Для меня проблема была другой; (Я был глуп) У меня было всплывающее окно с кнопкой входа в Facebook и снова кнопками входа в Facebook на странице входа/регистрации.

  • Всякий раз, когда я нажимаю кнопку Facebook во всплывающем окне с других страниц, это работает нормально.
  • Но, когда я нажимаю кнопку Facebook во всплывающем окне на странице входа/регистрации, это не сработает.

Причина заключалась в том, что я повторно создал объекты Facebook и getRedirectLoginHelper. Я должен был прокомментировать эти заявления на страницах входа/регистрации, как это уже было доступно.

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

Это не тот путь, по которому нужно идти. Хотя это сработало.

 -1
Author: Suriya, 2017-12-13 08:44:11