知识补充

因为yii2 csrf的验证的加解密 涉及到异或运算

所以需要先补充php里字符串异或运算的相关知识,不需要的可以跳过

^异或运算
不一样返回1 否者返回 0
在PHP语言中,经常用来做加密的运算,解密也直接用^就行
字符串运算时 利用字符的ascii码转换为2进制来运算
单个字符运算
举例的ascii见下表

字符

二进制

ASCII

a

1100001

97

b

1100010

98

c

1100011

99

d

1100100

100

计算结果

运算

二进制

ASCII

a^b

0000 0011

3

a^c

0000 0010

2

b^d

0000 0110

6

ab^cd

0000 0010

2

a^cd

0000 0010

2

ab^c

0000 0010

2

1.对于单个字符和单个字符的
直接计算其结果即可 比如表里的a^b

2.对于长度一样的多个字符串 如表里的ab^cd
计算a^c对应的结果和和b^d对应的结果 对应的字符连接起来

<?php
$str1='ab';
$str2="cd";
$r= $str1^$str2;
var_dump($r);
echo "<hr>";
for($i=0;$i<strlen($r) ;$i++){
echo ord($r[$i])."<br>";
}
?>

对于不等的
以短的字符串长度位进行计算

Yii2的csrf token验证
在yii2的接收post请求时
在如果开启
enableCsrfValidation为true
在/vendor/yiisoft/yii2/web/Controller.php

<?php
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
if ($this->enableCsrfValidation && Yii::$app->getErrorHandler()->exception === null && !Yii::$app->getRequest()->validateCsrfToken()) {
throw new BadRequestHttpException(Yii::t('yii', 'Unable to verify your data submission.'));
}
return true;
} return false;
}
?>

会进行validateCsrfToken验证
在/vendor/yiisoft/yii2/web/Request.php

<?php
public function validateCsrfToken($token = null)
{ $method = $this->getMethod();
// only validate CSRF token on non-"safe" methods http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
return true;
} $trueToken = $this->loadCsrfToken(); if ($token !== null) {
return $this->validateCsrfTokenInternal($token, $trueToken);
} else {
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
}
}
?>

说明在 GET, HEAD, OPTIONS 均不验证,除了这几种主要用的也就post了

说明在我们发送post请求时必须发送相关验证的字段和值
下面看CsrfToken产生过程
在/vendor/yiisoft/yii2/web/Request.php里

<?php
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, static::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;
}
?>

会发现
_csrfToken的产生大致如下
如果开启了enableCsrfCookie,
CsrfToken就从cookie里取,否者从session里取(更安全)
可在
/vendor/yiisoft/yii2/web/Request.php的下面部位看到

<?php
protected function loadCsrfToken()
{
if ($this->enableCsrfCookie) {
return $this->getCookies()->getValue($this->csrfParam);
} else {
return Yii::$app->getSession()->get($this->csrfParam);
}
}
?>

从loadCsrfToken()里取出的值这里称token

在post里发送的也就是Yii::$app->getRequest()->csrfParam 这里称csrfToken
现在根据代码大致说下生成和验证的主要思路,当然自己看代码更能细致的了解
1.从cookie或者session里取出token ,当然cookie或者session里如果没有就是初始化操作的过程了,这里初始化不是重点
2.随机产生CSRF_MASK_LENGTH(Yii2里默认是8位)长度的字符串 mask
3.对mask和token进行如下运算
str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask))); $this->xorTokens($arg1,$arg2) 是一个先补位异或运算

传入$arg1,$arg2
长度短的要用自身补到长度长的字符串的位置
见代码部分
在 /vendor/yiisoft/yii2/web/Request.php 的如下部分

 <?php
private function xorTokens($token1, $token2)
{
$n1 = StringHelper::byteLength($token1);
$n2 = StringHelper::byteLength($token2);
if ($n1 > $n2) {
$token2 = str_pad($token2, $n1, $token2);
} elseif ($n1 < $n2) {
$token1 = str_pad($token1, $n2, $n1 === 0 ? ' ' : $token1);
} return $token1 ^ $token2;
}
?>

就是说如果 $arg1比$arg2短,$arg1要用自身补齐 补到和和$arg2一样的长度
这里为什么要这样做?
因为在php里
'a'^'bc' 会只算 a^b 而不考虑c了,这里采用了向长度更长的来补
如果用
xorTokens来处理 'a'和'bc'
会先把a用自己填充到和bc一样的长度后再进行异或运算
异或运算详见上文补充

str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));

计算后即会得出在post请求时要发送的值 csrfToken

下面是验证过程
1.根据 表单字段名
Yii::$app->getRequest()->csrfParam;
从post里拿到
csrfToken的值
从方法 validateCsrfToken里可以看到
代码
在/vendor/yiisoft/yii2/web/Request.php 的如下部分

<?php
public function validateCsrfToken($token = null)
{ $method = $this->getMethod();
// only validate CSRF token on non-"safe" methods http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
return true;
} $trueToken = $this->loadCsrfToken(); if ($token !== null) {
return $this->validateCsrfTokenInternal($token, $trueToken);
} else {
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
}
} ?>

$this->getBodyParam($this->csrfParam)
可以看出
解密的目的就是要从
csrfToken里取出token 然后和会话里的token比较
见/vendor/yiisoft/yii2/web/Request.php 的如下部分

<?php
private function validateCsrfTokenInternal($token, $trueToken)
{ $token = base64_decode(str_replace('.', '+', $token));
$n = StringHelper::byteLength($token);
if ($n <= static::CSRF_MASK_LENGTH) {
return false;
}
$mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
$token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
/*
注意此时的$token在加密过程中是xorTokens($trueToken,$mask)的结果
*/
$token = $this->xorTokens($mask, $token); return $token === $trueToken;
}
?>

加密时用的是
str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));
解密
1.首先要把.替换成+
2.然后base64_decode
再 根据长度分别取出$mask和$this->xorTokens($token, $mask) ;
为了说明方便 $this->xorTokens($token, $mask) 这里称作 token1
然后
进行mask和token1的异或运算,即得token
注意在加密时
token1=token^mask
所以
解密时
token=mask^token1=mask^(token^mask)

yii2
中的核心思路
token是从会话中取得的
用随机串和token进行运算处理 得到一个加密串
验证的时候通过这个加密串解密出来这个token和会话里的值进行比较

yii2 csrf验证原理分析的更多相关文章

  1. yii2的csrf验证原理分析及token缓存解决方案

    本文主要分三个部分,首先简单介绍csrf,接着对照源码重点分析一下yii框架的验证原理,最后针对页面缓存导致的token被缓存提出一种可行的方案.涉及的知识点会作为附录附于文末. 1.CSRF描述 C ...

  2. 148.CSRF攻击原理分析、防御、装饰器、中间件、IFrame以及js实现csrf攻击

    CSRF攻击概述: CSRF(Cross Site Request Forgery 跨站域请求伪造)是一种网站攻击的方式,它在2007年曾被列为互联网20大安全隐患之一.其他的安全隐患,比如SQL脚本 ...

  3. Yii2 Restful API 原理分析

    Yii2 有个很重要的特性是对 Restful API的默认支持, 通过短短的几个配置就可以实现简单的对现有Model的RESTful API 参考另一篇文章: http://www.cnblogs. ...

  4. Django CSRF 原理分析

    原文链接: https://blog.csdn.net/u011715678/article/details/48752873 参考链接:https://blog.csdn.net/clark_fit ...

  5. Yii2 关闭和打开csrf 验证 防止表单多次重复提交

    原文地址:http://blog.csdn.net/terry_water/article/details/52221007 1.在Yii2配置中配置所有:所有的controller都将关闭csrf验 ...

  6. Django中csrf token验证原理

    我多年没维护的博客园,有一篇初学Django时的笔记,记录了关于django-csrftoekn使用笔记,当时几乎是照抄官网的使用示例,后来工作全是用的flask.博客园也没有维护.直到我的博客收到了 ...

  7. yii2表单提交CSRF验证

    Yii2表单提交默认需要验证CSRF,如果CSRF验证不通过,则表单提交失败,解决方法如下: 第一种解决办法是关闭Csrf public $enableCsrfValidation = false; ...

  8. flask提交表单验证不通过,以及CSRF攻击原理

    学习表单的问题1. 提交表单时怎么都无法验证通过 记录一下,自己的学习bug,主要是因为在模板中书写渲染的语句时,把CSRF的字段名写错了. 因为在模板中书写一些语句是没有提示的,自己手动敲代码容易出 ...

  9. Yii2 CSRF

    一.CSRF 即Cross-site request forgery跨站请求伪造,是指有人冒充你的身份进行一些恶意操作. 比如你登录了网站A,网站A在你的电脑设置了cookie用以标识身份和状态,然后 ...

随机推荐

  1. java.lang.IllegalArgumentException异常处理的一种方法

    我遇到的问题 用spring注解来加载bean,都是一个简单的了,但是还是报了这样一个错java.lang.IllegalArgumentException 网上查了一下,说是jdk版本要1.7的,然 ...

  2. JAVA集合类汇总 - 转载

    一.集合与数组 数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用. 集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用. ...

  3. WebSocket相关

    原文:http://www.cnblogs.com/jinjiangongzuoshi/p/5062092.html 前言 今天看了一些资料,记录一下心得. websocket是html5引入的一个新 ...

  4. Pandas索引和选择数据

    在本章中,我们将讨论如何切割和丢弃日期,并获取Pandas中大对象的子集. Python和NumPy索引运算符"[]"和属性运算符".". 可以在广泛的用例中快 ...

  5. BZOJ 1835 [ZJOI2010]base 基站选址:线段树优化dp

    传送门 题意 有 $ n $ 个村庄在一排直线上,现在要建造不超过 $ K $ 个通讯基站,基站只能造在村庄处. 第 $ i $ 个村庄距离第 $ 1 $ 个村庄的距离为 $ D_i $ .在此建造基 ...

  6. Python StringIO实现内存缓冲区中读写数据

    StringIO的行为与file对象非常像,但它不是磁盘上文件,而是一个内存里的“文件”,我们可以像操作磁盘文件那样来操作StringIO.这篇文章主要介绍了Python StringIO模块,此模块 ...

  7. WPF 自定义依赖属性

      原博客地址:http://www.cnblogs.com/DebugLZQ/archive/2012/11/30/2796021.html    DependencyObject和Dependen ...

  8. 分享知识-快乐自己:Ajax 跨域请求处理

    <%-- Created by IntelliJ IDEA. User: asus Date: 2019/1/24 Time: 15:57 To change this template use ...

  9. vue 时间格式化

    export function formatDate(date, fmt) { if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.g ...

  10. ipmitool 查看硬件信息

    [root@75-6-25-yf-core ~]# cat /var/log/mcelog MCE 0HARDWARE ERROR. This is *NOT* a software problem! ...