Facebook SDK вернул ошибку: проверка подделки межсайтовых запросов не удалась. Параметр «state» из URL и сессии не совпадают

48

Я пытаюсь получить идентификатор пользователя 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 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 благодарим за любую помощь заранее.

  • 1
    Для Laravel 5.2 у меня тоже есть эта ошибка: «Подделка межсайтовых запросов не удалась. Требуемый параметр« состояние »отсутствует». и это то, что я сделал, чтобы решить это. мой ответ
  • 0
    @ Фади, пожалуйста, предоставьте принятый ответ, как enobrev один, потому что есть много людей, которые дают неправильный ответ, деактивируя безопасность
Показать ещё 1 комментарий
Теги:
facebook
facebook-graph-api

25 ответов

89
Лучший ответ

Я обнаружил, что до тех пор, пока я разрешал сеансы PHP до создания URL-адреса входа, а в верхней части script 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 по умолчанию не всегда будет работать.

  • 2
    Это просто сработало!
  • 1
    Не знаю, почему это сработало, но это сработало. Спасибо!
Показать ещё 8 комментариев
60

введите этот код после $ helper = $fb- > getRedirectLoginHelper();

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

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

  • 2
    Это просто решило проблему. Почему кто-то отрицал этот ответ!?!?!?
  • 0
    я не знаю почему
Показать ещё 11 комментариев
44

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

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

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

$helper = $fb->getRedirectLoginHelper();

Добавьте код ниже,

if (isset($_GET['state'])) {
    $helper->getPersistentDataHandler()->set('state', $_GET['state']);
}
  • 0
    где я должен добавить дополнительный код, в login.php или callback.php?
  • 0
    Вы должны добавить это в файл nextend-facebook-connect.php, который вы найдете в папке плагинов, если вы используете WordPress
Показать ещё 5 комментариев
19

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

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

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

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

12

Это происходит, когда библиотека 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

  • 0
    Спасибо, это помогло мне и в случае с Yii2!
  • 0
    Я также написал свою собственную библиотеку для CodeIgniter. Я использую FILE кеширование.
Показать ещё 1 комментарий
8

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

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

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

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

  • 0
    Я вижу, как это работает, но это так скучно. Конечно, должна быть альтернатива, а не переводить данные из $ _SESSION в $ _COOKIE, а затем обратно в $ _SESSION?
  • 1
    Это определенно не тот путь, передайте вашу собственную реализацию PersistentDataInterface в конфигурации, смотрите здесь: developers.facebook.com/docs/php/PersistentDataInterface/5.0.0
7

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

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

4

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

session_start();

в верхней части script.

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

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

проблема возникает, потому что она еще не началась, добавив начало сеанса в начале script, мы только начинаем сеанс.

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

3

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

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

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

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

$_SESSION['FBRLH_' . 'state']

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

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

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

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

после

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

и до

$accessToken = $helper->getAccessToken();

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

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

  • 0
    Ваша отладка верна, но решения нет, только отладка.
2

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

У меня была эта проблема некоторое время, и я искал вокруг и видел много разных решений, многие из которых отключили проверку 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 и без него и почему используется SERVER_NAME?

потому что ваш Действительный 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 ['SERVER_NAME'] вернет домен с или без www в зависимости от того, как пользователь вводил URL-адрес в браузере.

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

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

  • 0
    Это помогает мне. Я должен был добавить session_start()
2

С 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

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

<?php
if(isset($_GET['state'])) {
      if($_SESSION['FBRLH_' . 'state']) {
          $_SESSION['FBRLH_' . 'state'] = $_GET['state'];
      }
}
?>
2

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

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

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

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

Фиксированная версия:

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

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

date_default_timezone_set('Europe/Istanbul');

до script. Работал как шарм. Для проверки местоположения: timezones.europe.php

1

Легкое исправление для меня. Я изменил:

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

в

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

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

  • 1
    Это не то, как решить эту проблему. Например, я использую сайты Azure для тестирования некоторых вещей с помощью FB SDK, и я использую автоматически сгенерированный URL (xxxx.azurewebsites.net), и я получаю сообщение об ошибке, даже если источник и цель совпадают.
0

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

0

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

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

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

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

- это не путь. Хотя это сработало.

0

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

Полный код (в URL-адресе перенаправления)

$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

РЕШЕНИЕ ДЛЯ ИНТЕРМЕТЕНТНЫХ ПРОБЛЕМ

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

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

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

$_ SESSION ['FBRLH_state'] = $_GET ['state'];

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

0

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

getJavaScriptHelper();

вместо

getRedirectLoginHelper();
0

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

При обратном вызове script обычно 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();
} ?>
0

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

0

Решение 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

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

  if($_SERVER['HTTP_HOST']!=='www.website.com'){
  header('location: http://www.website.com');
  }
-2

см. ваш "default_graph_version" на всех страницах, если изменить, а затем сохранить их правильно, это показано в https://developers.facebook.com/apps/

Ещё вопросы

Сообщество Overcoder
Наверх
Меню