csrf是什么?
csrf全称:跨站点请求伪装(cross site request forgery)。第三方站点利用正常站点的登录凭证绕过后端身份验证,达到攻击该站点的目的。
为什么会产生csrf攻击呢?
说这个之前,我们得先知道另外一个东西,cookie, 它一般是用于浏览器存储用户的登录凭证信息。当有数据需要发送给相关站点时,会带上浏览器中该站点相关cookie信息,以达到身份校验的目的。
我们需要知道的几点:
1.csrf一般发生在跨站请求上,因为第三方站点更容易被“操作”。
2.cookie信息不能盗取,只能“使用”
常用的csrf防御手段
1.csrf token
这种方式是比较常见的一种
做法:
在提交内容或请求头中隐藏一个token值,token值是一次性的,确保其不可预测性,然后在服务端校验该token值。
缺点:
对于前后端分离的站点,token值不能直接渲染到前端页面上。
2.图片验证码
3.手机验证码
这些防御措施都是用来增加破解的难度,提升系统的安全性。
Yii2.0 中的csrf token实现方案
1.页面渲染
<input name="_csrf" type="hidden" id="_csrf" value="<?= Yii::$app->request->csrfToken ?>">
2.生成token
protected function generateCsrfToken()
{
$token = Yii::$app->getSecurity()->generateRandomString();
if ($this->enableCsrfCookie) {
$config = $this->csrfCookie;
$config['name'] = $this->csrfParam;
$config['value'] = $token;
Yii::$app->getResponse()->getCookies()->add(new Cookie($config));
} else {
Yii::$app->getSession()->set($this->csrfParam, $token);
}
return $token;
}
public function generateRandomString($length = 32)
{
$bytes = $this->generateRandomKey($length);
// '=' character(s) returned by base64_encode() are always discarded because
// they are guaranteed to be after position $length in the base64_encode() output.
return strtr(substr(base64_encode($bytes), 0, $length), '+/', '_-');
}
1.服务端线生成一个随机字符串,长度为32
2.对1)中字符串进行base64编码、然后截取前32位字符串,并把‘+’ 、‘/’ 进行替换
3.将2)中字符串保存到session中(这里假设服务端用的是session存储token值)
上面3步生成了服务端使用的token值,返回客户端时,还需要做一些运算
public function getCsrfToken($regenerate = false)
{
if ($this->_csrfToken === null || $regenerate) {
if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
$token = $this->generateCsrfToken();
}
// the mask doesn't need to be very random
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
$mask = substr(str_shuffle(str_repeat($chars, 5)), 0, self::CSRF_MASK_LENGTH);
// The + sign may be decoded as blank space later, which will fail the validation
$this->_csrfToken = str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));
}
return $this->_csrfToken;
}
这一步主要是将服务端session中使用的token,做了一次加密处理,不直接对客户端暴露真实token。
处理如下:
1.生成8位长度的随机掩码mask
2.将token与mask做一个异或运算后,并将掩码拼接在字符串最前端
3.对2)中生成的字符串进行base64编码,并把 ‘+’ 替换成 ‘.’
由此,生成了页面上的csrf token。
那接下来服务端是怎么校验这个csrf token呢?
3.校验csrf token
private function validateCsrfTokenInternal($token, $trueToken)
{
$token = base64_decode(str_replace('.', '+', $token));
$n = StringHelper::byteLength($token);
if ($n <= self::CSRF_MASK_LENGTH) {
return false;
}
$mask = StringHelper::byteSubstr($token, 0, self::CSRF_MASK_LENGTH);
$token = StringHelper::byteSubstr($token, self::CSRF_MASK_LENGTH, $n - self::CSRF_MASK_LENGTH);
$token = $this->xorTokens($mask, $token);
return $token === $trueToken;
}
1.获取客户端csrf token值,将 ‘.’ 替换为 ‘+’,然后做base64解码
2.取出前8位字符串,这个字符串就是对应的掩码mask
3.从第9位直到最后就是做过异或运算之后的结果
4.将3)中字符串与mask做一次异或运算,此时得到的就是解密后的token值
5.将该token值与session中token进行对比,若一致,则csrf token验证通过,反之,则验证失败
4.小结
Yii2.0中csrf token生成规则 是生成随机字符串token,然后与掩码异或运算并拼接,再做base64编码。
解码规则就是对生成做一个逆运算,这里比较巧妙的地方是与掩码做异或运算,异或运算是可以逆运算的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。