Facebook PHP SDK 5 :: API 2.4 :: Ошибка проверки подделки межсайтового запроса. Отсутствует требуемое состояние параметра

Я сделал очень простой PHP-скрипт, просто чтобы попытаться войти через Facebook и получить accessToken. Но когда я пробую следующий код, я получаю исключение из SDK: «Проверка подделки межсайтового запроса не удалась. Отсутствует обязательный параметр "состояние". ».

Вот мой код:

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;

person Éric Senterre    schedule 20.07.2015    source источник
comment
Проверьте, правильно ли работает ваш сеанс PHP (передача идентификатора сеанса).   -  person CBroe    schedule 21.07.2015
comment
Это то, что я сделал. И, наконец, обнаружил, что мой файл PHP был закодирован в UTF8 вместо UTF8 без спецификации. Спасибо за вашу помощь.   -  person Éric Senterre    schedule 21.07.2015


Ответы (23)


Мне пришлось добавить эти строки на некоторых серверах:

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

Я получаю эту ошибку случайным образом, в зависимости от конфигурации сервера.

person mdalda    schedule 04.08.2016
comment
Я боролся с этой проблемой в течение нескольких часов, и это единственное решение, которое работает для меня. Спасибо чувак! - person grpaiva; 26.10.2016
comment
Оно работало завораживающе. Это лучший ответ. Спасибо - person Duy Hoang; 13.02.2017
comment
Не делайте этого! Это эффективно обходит проверку CSRF. - person tomasbedrich; 25.07.2017

session_start() в начале обоих скриптов. Я получил решение отсюда: https://github.com/facebook/facebook-php-sdk-v4/issues/473

person Ruhul Amin    schedule 08.02.2016

Если вы используете версию своего сайта с «www» для создания ссылки для входа и вас перенаправляют на версию без www, у вас возникнут проблемы с сеансом. Поэтому убедитесь, что вы обращаетесь к www-версии своего сайта, а затем определяете URL-адрес обратного вызова для той же www-версии. Кроме того, вы можете определить постоянные перенаправления в конфигурации вашего сервера, чтобы любой, кто заходит на ваш сайт с версии без www, перенаправлялся на версию с www или наоборот.

person Cedric Ipkiss    schedule 27.10.2015

Я также столкнулся с той же проблемой, и после исследования строки ввода stackoverflow

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

выше решил мою проблему

$helper = $fb->getRedirectLoginHelper();  
person ashutosh    schedule 14.09.2016
comment
Имейте в виду, что это решение обходит проверку CSRF, установленную API. - person Phyllis Sutherland; 29.09.2017

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

$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);
person Jean.R    schedule 03.01.2016

Ларавел 5.2

У меня тоже есть эта ошибка «Проверка подделки межсайтового запроса не удалась. Отсутствует обязательный параметр «состояние»».

и после нескольких часов чтения этого. Я попытался изменить сценарий поставщика.

в 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);
}

в (я добавляю 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.');
    }
}

в (я добавил $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.');
    }
}

это все, что я сделал. и ошибка ушла.

person Nanang Koesharwanto    schedule 18.04.2016
comment
Слишком много, чтобы измениться. - person CodeGuru; 19.07.2016
comment
Вы не должны изменять файлы поставщиков! - person andrewtweber; 21.05.2017
comment
@andrewtweber: у меня больше нет вариантов, так как я уже пробовал несколько методов, и это единственный метод, который работает в моем случае. - person Nanang Koesharwanto; 25.10.2017
comment
мы все знаем, что мы не можем изменить файлы поставщиков, но это решение решило эту проблему! - person Leonardo Max; 20.09.2020
comment
@CodeGuru, я так не думаю, потому что он добавляет только одну строку script в функцию makeUrl и одну строку в функцию validateCsrf. - person Nanang Koesharwanto; 14.01.2021

Явная установка [PersistentDataHandler] перед получением токенов гарантирует, что запрос будет успешным. Ниже показано, как получить $_GET['state'] и просто внедрить его в «используемый [помощник]» в Symfony 2.x | 3.х

У меня была та же проблема, что и у 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'));
person Dan Baddeley    schedule 22.03.2017

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

В сценарии обратного вызова обычно fb-callback.php добавляется "session_start();" непосредственно перед тем, как включить файл автозагрузки facebook. а затем "$_SESSION['FBRLH_state']=$_GET['state'];" после "$helper = $fb->getRedirectLoginHelper();" линия.

Пример :

<?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();
} ?>
person Ricky Harerri    schedule 30.05.2017
comment
Имейте в виду, что такое решение нарушает защиту CSRF. Потому что защита основана на сравнении сохраненного токена и полученного. Предоставленный код заставляет их сопоставляться - его перезаписывает сохраненный токен полученным. - person 3DFace; 27.07.2017

Настоящей проблемой здесь была кодировка моего файла PHP. Я использовал UTF8, но это должна была быть UTF8 без спецификации.

Побочным эффектом неправильной кодировки было то, что моя СЕССИЯ не работала должным образом, а затем SDK не мог получить необходимую информацию для правильной работы.

Правильно настроенный отчет об ошибках сразу сказал бы о наличии проблемы.

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

person Éric Senterre    schedule 21.07.2015
comment
Привет Эрик, у меня такая же ошибка. Сам немного нуб. Клянусь, несколько дней назад все работало нормально, но теперь у меня такая же ошибка, как и у вас. Мой провайдер использует UTF-8 (default_charset)... вы говорите, что это нужно изменить на UTF8 без спецификации, чтобы вход в систему работал? Спасибо - person Tatters; 21.07.2015
comment
Да, вашему файлу PHP нужна кодировка UTF8 без спецификации. В противном случае файл PHP отправит символ до того, как session_start() сможет создать свои файлы cookie в заголовке HTTP (или что-то в этом роде). Я столкнулся с этой проблемой отчасти потому, что я новый пользователь Notepad++. И я вслепую установил свою кодировку на UTF8, потому что французские акценты не отображались правильно на моей странице, как я делал это в EditPlus. Таким образом, YES устанавливает кодировку файла PHP в UTF8 без спецификации. Я не могу гарантировать, что это решит вашу проблему, но решило мою. - person Éric Senterre; 21.07.2015
comment
Хороший, спасибо. Я попробую и посмотрю, что произойдет. - person Tatters; 21.07.2015

Для меня проблема решена сейчас, просто запустив сеанс, добавив это:

session_start();

в начале обоих файлов (первый файл генерирует URL-адрес facebook и файл обратного вызова: login.php и fb-callback.php (https://developers.facebook.com/docs/php/howto/example_facebook_login)).

Я также должен был добавить это:

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

в верхней части, чтобы предотвратить другую несвязанную ошибку.

person Adam Tong    schedule 02.01.2016

Как я ответил здесь: >Facebook SDK вернул ошибку: Ошибка проверки подделки межсайтового запроса. Параметр состояния из URL и сеанса не совпадают

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

person Chao Chen    schedule 22.01.2016

Для меня это происходило из-за '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'
  ]);
person Amit Tyagi    schedule 25.06.2016

Я столкнулся с подобной проблемой и нашел решение Nanang Koesharwanto. Это больше похоже на причуду, поскольку изменение исходных файлов в каталоге поставщика — очень плохая идея. Вот в чем хитрость.

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

Если это не удается, поместите use Session; в свой контроллер.

person Rajender Joshi    schedule 03.06.2016
comment
Имейте в виду, что это решение обходит проверку CSRF, установленную API. - person Phyllis Sutherland; 29.09.2017

Наконец, изучив код FB, я обнаружил, что проблема "Сбой проверки подделки межсайтового запроса. Отсутствует обязательный параметр "state"" и тому подобное вызвана переменной 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 часов работы :) Пока, Алекс.

person ale500    schedule 15.08.2015

вы должны убедиться, что сеанс начинается до запуска скрипта. но снова он выдаст 443: Network is unreachable, если вы снова начнете сеанс в том же скрипте. надеюсь, это поможет кому-то.

Я только что использовал if (session_status() == PHP_SESSION_NONE){ session_start(); }

person Tharindu    schedule 24.03.2016

Для тех из вас, кто использует 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();
person Emastmagy MastMagy    schedule 30.05.2016

Исправление для меня состояло в том, чтобы изменить 'secure' => true на false в config/session.php. Я случайно установил для этого значение true, не используя https в первую очередь.

person Jonas Menk    schedule 22.07.2016

Для меня решением было поймать исключение, заменив пространство имен 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;
}
person michalzuber    schedule 30.08.2016

Если session_start() по-прежнему не решает проблему, вы указали неправильный URI в допустимом URI перенаправления OAuth вашего приложения для разработчиков Facebook.

Чтобы решить: во-первых, перейдите в свой fb-callback.php, найдите URI, который вы указали в функции заголовка, например: header("Location: https://localhost/home.php"). Затем перейдите в свои приложения для разработчиков Facebook, а затем на боковой панели нажмите «Вход через Facebook», которая находится на вкладке «Продукты», затем нажмите «Настройки». В допустимых URI перенаправления OAuth добавьте URI, указанный в заголовке. В моем примере я поставлю https://localhost/home.php.

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

person John Joshua Villegas    schedule 21.11.2018

Я решил эту ошибку, установив свой URL-адрес обратного вызова в «Facebook -> Логин -> Действительные URI перенаправления OAuth» в учетной записи разработчика facebook.

person Tarak Gandhi    schedule 08.05.2020

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

SDK V5 и APi 2.4 работают как в этом tutorial описан, токен доступа необходимо определить

Обязательно заполните 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 для разработчиков.

Повеселись :)

person RakonDark    schedule 20.10.2015

Что бы ты не делал. Просто не вызывайте getAccessToken() более одного раза. Он удаляет состояние из сеанса, как только оно вызывается.

person Jasmeet Singh    schedule 15.05.2017