it-swarm.com.ru

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

51
Fadi

Я обнаружил, что до тех пор, пока я включал сеансы PHP до генерации URL-адреса входа в систему, а также в верхней части скрипта, на который в конечном итоге перенаправляет Facebook, он прекрасно работает сам по себе, не устанавливая cookie ( согласно ale500 ответ ). Это использует версию 5.1 SDK.

В верхней части обоих сценариев я добавил ...

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 по умолчанию не всегда работает.

93
enobrev

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

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

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

63
zratan

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

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

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

$helper = $fb->getRedirectLoginHelper();

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

if (isset($_GET['state'])) {
    $helper->getPersistentDataHandler()->set('state', $_GET['state']);
}
47
G.Ashok Kumar

Я получил эту ошибку при использовании Facebook SDK в 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, так как обработчик памяти работает только при использовании SDK исключительно в обычных контроллерах/сервисах.

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

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

20
Fx32

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

Пример конструктора params:

$fb = new Facebook\Facebook([
  // . . .
  'persistent_data_handler' => new MyLaravelPersistentDataHandler(),
  // . . .
 ]);

Более подробная информация здесь: https://developers.facebook.com/docs/php/PersistentDataInterface/5.0.0

14
George

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

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

и подобное вызвано переменной 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);

И в 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
ale500

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

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

7
Nino Škopac

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

session_start();

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

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

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

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

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

4
usama

У объекта 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-символьная строка. 

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

3
Chao Chen

С 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
user4520510

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

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

потому что ваш Valid OAuth-редирект URI должен соответствовать вашему 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 и пока никаких проблем.

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

2
BlueSun3k1

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

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

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

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

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

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

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

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

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

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

чтобы:

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

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

1
Mathieu Wilson

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

date_default_timezone_set('Europe/Istanbul');

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

1
Roman Losev

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

Может помочь кому-то, кто использует Javascript Helper во внешнем интерфейсе для аутентификации пользователя, а в PHP каждый пытается извлечь access_token from Redirect Login Helper . Так что используйте следующее

getJavaScriptHelper();

вместо

getRedirectLoginHelper();
0
makki

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

Полный код (в URL перенаправления 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
digitalzoomstudio

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

В сценарии обратного вызова обычно 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
Er Prabhjot Singh Tugger

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

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

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

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

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

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

0
Phyllis Sutherland

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

0
Umbrella Brothers

Ошибка возникает, если имя хоста источника отличается от имени хоста назначения [как упоминалось в 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');
  }
0
Piya Poonsawat