CSRF状态令牌与FB PHP SDK 3.1.1 Oauth 2.0提供的令牌不匹配。

24

我的服务器日志显示"CSRF状态令牌与提供的不匹配"错误,几乎每个用户都会出现这个问题。但是,用户已经被创建和/或身份验证,并且我能够检索到用户信息。我正在使用带有Apache的Linux服务器。我也在使用最新的Facebook PHP SDK v.3.1.1。有人可以告诉我为什么会发生这种情况以及如何解决吗?


1
我还想补充一点,最近我也遇到了同样的问题。用户可以按预期使用应用程序,但是我的洞察力在2月14日后突然停止报告。通过查看我的日志,我看到了你所遇到的完全相同的错误。 - Alec Sanger
9个回答

36
上周我也遇到了类似的问题,跟踪发现问题出在state字段被多次调用getLoginUrl()覆盖。每次调用getLoginUrl()都会在SDK中生成一个新的state令牌并存储在$_SESSION中(它只是一个随机值),所以如果你调用两次并且用户使用第一个链接登录,第二个调用会重置SDK内部的state令牌,然后你将在日志中收到此错误。
SDK在Facebook授权用户并重定向回您的网站时查找相同的state令牌,如果不匹配,则记录此错误(这里是源代码链接)。

@Jakub Facebook因为某种原因移动了代码库...更新了链接。我不再是PHP SDK的活跃用户,所以我不知道这是否仍然是一个问题。但还是希望能对你有所帮助。 - jches
@chesles,这仍然是一个非常大的问题,我现在遇到了它! - justinhartman
@chesles 请查看我的答案。 - Jimmy Kane
@JimmyKane,我在这里没有看到你的回答...是在另一个问题上吗?另外,我应该寻找什么? - jches
@chesles的回答已被删除。正在核实。 - Jimmy Kane
我解决这个问题的方法是在页面顶部添加“session_start”,在任何HTML输出之前。这避免了对getLoginUrl()的不必要调用。 - SSH This

9

在同一处理程序中两次检查令牌时,Facebook SDK代码存在错误。

我修改了facebook.php文件中的 getCode函数,像这样:

protected function getCode() {
    if (!isset($_REQUEST['code']) || !isset($_REQUEST['state']) || $this->state === null) {
      return false;
    }
    if ($this->state === $_REQUEST['state']) {
        // CSRF state has done its job, so clear it
        $this->state = null;
        $this->clearPersistentData('state');
        return $_REQUEST['code'];
    }
    self::errorLog('CSRF state token does not match one provided.');

    return false;
}

为了更清晰,如果调用两次不会显示无效令牌。

为了明确,如果在同一个URL处理程序上两次调用函数,则可以调用例如:$facebook->getUser();然后在同一个处理程序中调用$facebook->getLogoutUrl(),然后调用getCode()两次,从而导致无效错误消息。


如果有人想合并,这里也有一个拉取请求可以修复这个问题:https://github.com/facebook/facebook-php-sdk/pull/122 - Jimmy Kane
作为第二个注释,请保留此代码。尽管在 fb 存储库中合并时,它被覆盖了,但仍有另一个被拒绝的提交。 - Jimmy Kane

3

嗯,我曾经遇到过这个问题,我的问题在于URL中的statecode参数 - 我的.htaccess文件没有将它们转发。

我猜你也遇到了同样的问题。

CSRF状态令牌与提供的不匹配

希望这有所帮助。


@codecowboy 这个问题的具体解决方案取决于你的重写规则。主要是确保你传递了查询字符串参数。 - Brad

2
为了补充chesles的回答,这个问题可能是因为像我这样使用session_start()- session_write_close()函数时发生的。
如果您在请求loginUrl时没有启动会话,则会出现此错误。
附注:为什么要停止会话?
使用会话的脚本彼此停止,因为它们正在等待会话数组可用于使用。
想象一下,您有一个受欢迎的应用程序,有成千上万的用户,并且有一个操作(php脚本),在其中可以发布图片。 类似于这样:
--在脚本顶部开始会话
--连接到Facebook
--创建图像
--使用API调用共享图像
--脚本结束,会话自动关闭
这样做,会话将由脚本长时间无缘无故地使用。 对于这种脚本要小心,改为使用以下方式:
--在创建Facebook对象之前开始会话
--连接到Facebook
--通过session_write_close()关闭会话,会话数组可用,其他脚本可以加载
--创建图像
--使用API调用共享图像/*我认为这不需要会话*/
--脚本结束,已手动关闭会话。
干杯。

2

需要注意的一点是,虽然Facebook PHP API文档中没有明确说明,但你必须为PHP会话配置apache才能使登录过程正常工作。这就是当我们遇到“CSRF状态令牌与提供的令牌不匹配”错误时遇到的问题。

如果您正在使用服务器池,请确保已设置为使用memcache进行会话信息,否则apache将在本地编写会话信息,如果下一个请求不发送到同一台服务器,则会收到“CSRF状态令牌与提供的令牌不匹配”的错误消息。

这是其中一件在开发环境(使用一台服务器)中完美运行但在生产环境中失败的事情。

我们还必须重新配置CDN设置,以确保通过PHP会话cookie。


2
我遇到了同样的问题。解决方法很简单。不要调用
$fbLoginUrl = $facebook->getLoginUrl(...);

之前

$fbUser = $facebook->getUser();

否则您将会得到“CSRF 状态令牌与所提供的不匹配”错误。

1
我在本地机器上遇到了同样的问题,问题原因是我的hosts文件阻止了与Verisign的通信,所以Facebook尝试进行通信的URL(http://crl.verisign.com/pca3.crl)从未工作过(状态:404)。
从我的hosts文件中注释掉各种Verisign IP地址就解决了问题!

0
如果您在页面上使用.htaccess mod rewrite重定向,请在行末使用[QSA](查询字符串附加)以保留GET变量,否则您将丢失$code变量,这是Facebook登录所必需的。

0

本地会话检查 CSRF 状态和代码,我敢打赌您需要检查您的 php.ini 中的 session.save_handler 是否正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接