我使用Codeigniter/PHP。我使用CSRF令牌(不是CI原生版本,因为我有自己的表单实现),并且令牌不时不被验证。
CSRF令牌每个会话创建一次:
function create_csrf_token() //If needed, creates a session variable; returns a hash to use for CSRF protection
{
$CI =& get_instance();
if($CI->session->userdata('csrfToken')) //the token already exists: use its hash
{
$csrfHash = $CI->session->userdata('csrfToken');
}
else //no token yet: create session variable + set its hash
{
$csrfHash = base64_encode(hash('sha256', uniqid(serialize($_SERVER), true), true));
$CI->session->set_userdata(array('csrfToken' => $csrfHash));
}
return $csrfHash;
}
它在csrfToken隐藏输入字段中传递给表单,没有问题,htmlspecialchars
应用于它(使用urlencode
没有区别):
echo '<input type="hidden" name="'.$this->name.'" value="'.htmlspecialchars($this->value).'">';
该字段有一个验证规则verify_csrf
:
public function verify_csrf($token)
{
$CI =& get_instance();
if($CI->session->userdata('csrfToken') && $CI->session->userdata('csrfToken') == $token) return true;
else
{
$this->set_message('verify_csrf', 'Invalid token');
return false;
}
}
这就是事情变得奇怪的地方。有时$token
是不正确的,看起来像损坏的数据。下面是几个例子:
误差:
$CI->session->userdata('csrfToken')
: 6cT3O0KTOk7cVlear71lU7KKFlGONt4rS2HjNoSVFRM=
(正确)
$token
中的值:6cT O0KTOk7cVlear71lU7KKFlG
(第4个字符改变且缺少字符串结束)
没有错误:
$CI->session->userdata('csrfToken')
: AiAgGqqxTxuCxN7h5HHRtcJjmHJVMRksBYbq6Dx4Kv4=
中的值
$token
: AiAgGqqxTxuCxN7h5HHRtcJjmHJVMRksBYbq6Dx4Kv4=
中的值
任何想法?我已经检查并重新检查了,除了我的验证回调中的$token之外,CRSF令牌在任何地方都是正确设置的。它只发生在某些令牌上…
编辑:所以似乎是base64编码导致了这个问题(为什么,我不知道)。我已经替换了$csrfHash = base64_encode(hash('sha256', uniqid(serialize($_SERVER), true), true));
$csrfHash = random_string('sha1');
这只是一个大胆的猜测,但它可能是Base64编码和通过HTTP POST提交表单的组合,就像下面描述的那样:
POST Base64编码的PHP数据
解决方案可以是在发布之前urlencode()令牌?
EDIT:解决方案是放弃令牌的base64编码,转而使用普通的sha256哈希作为令牌。
请使用CSRF的配置
你可以打开你的应用/config/config.php文件,并设置如下:
$config['csrf_protection'] = TRUE;
//这将自动生成form_open(params)
函数的隐藏字段,因此您不需要每次都这样做。此功能包含在框架工作中。
避免对特定uri的CSRFhttp://ellislab.com/forums/viewthread/182631/