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

1.CSRF描述

CSRF全称为“Cross-Site Request Forgery”,是在用户合法的SESSION内发起的攻击。黑客通过在网页中嵌入Web恶意请求代码,并诱使受害者访问该页面,当页面被访问后,请求在受害者不知情的情况下以受害者的合法身份发起,并执行黑客所期待的动作。以下HTML代码提供了一个“删除产品”的功能:

  1. <a href="http://www.shop.com/delProducts.php?id=100" "javascript:return confirm('Are you sure?')">Delete</a>

假设程序员在后台没有对该“删除产品”请求做相应的合法性验证,只要用户访问了该链接,相应的产品即被删除,那么黑客可通过欺骗受害者访问带有以下恶意代码的网页,即可在受害者不知情的情况下删除相应的产品。

2.yii的csrf验证原理 /vendor/yiisoft/yii2/web/Request.php简写为Request.php

/vendor/yiisoft/yii2/web/Controller.php简写为Controller.php

开启csrf验证

在控制器里将enableCsrfValidation为true,则控制器内所有操作都会开启验证,通常做法是将enableCsrfValidation为false,而将一些敏感操作设为true,开启局部验证。

  1. public $enableCsrfValidation = false;
  2. /**
  3. * @param \yii\base\Action $action
  4. * @return bool
  5. * @desc: 局部开启csrf验证(重要的表单提交必须加入验证,加入$accessActions即可
  6. */
  7. public function beforeAction($action){
  8. $currentAction = $action->id;
  9. $accessActions = ['vote','like','delete','download'];
  10. if(in_array($currentAction,$accessActions)) {
  11. $action->controller->enableCsrfValidation = true;
  12. }
  13. parent::beforeAction($action);
  14. return true;
  15. }

生成token字段

在Request.php

首先通过安全组件Security获取一个32位的随机字符串,并存入cookie或session,这是原生的token.

  1. /**
  2. * Generates an unmasked random token used to perform CSRF validation.
  3. * @return string the random token for CSRF validation.
  4. */
  5. protected function generateCsrfToken()
  6. {
  7. $token = Yii::$app->getSecurity()->generateRandomString();
  8. if ($this->enableCsrfCookie) {
  9. $cookie = $this->createCsrfCookie($token);
  10. Yii::$app->getResponse()->getCookies()->add($cookie);
  11. } else {
  12. Yii::$app->getSession()->set($this->csrfParam, $token);
  13. }
  14. return $token;
  15. }

接着通过一系列加密替换操作,生成加密后_csrfToken,这个是传给浏览器的token. 先随机产生CSRF_MASK_LENGTH(Yii2里默认是8位)长度的字符串 mask

对mask和token进行如下运算 str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask))); $this->xorTokens($arg1,$arg2) 是一个先补位异或运算

  1. /**
  2. * Returns the XOR result of two strings.
  3. * If the two strings are of different lengths, the shorter one will be padded to the length of the longer one.
  4. * @param string $token1
  5. * @param string $token2
  6. * @return string the XOR result
  7. */
  8. private function xorTokens($token1, $token2)
  9. {
  10. $n1 = StringHelper::byteLength($token1);
  11. $n2 = StringHelper::byteLength($token2);
  12. if ($n1 > $n2) {
  13. $token2 = str_pad($token2, $n1, $token2);
  14. } elseif ($n1 < $n2) {
  15. $token1 = str_pad($token1, $n2, $n1 === 0 ? ' ' : $token1);
  16. }
  17. return $token1 ^ $token2;
  18. }
  19. public function getCsrfToken($regenerate = false)
  20. {
  21. if ($this->_csrfToken === null || $regenerate) {
  22. if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
  23. $token = $this->generateCsrfToken();
  24. }
  25. // the mask doesn't need to be very random
  26. $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
  27. $mask = substr(str_shuffle(str_repeat($chars, 5)), 0, static::CSRF_MASK_LENGTH);
  28. // The + sign may be decoded as blank space later, which will fail the validation
  29. $this->_csrfToken = str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));
  30. }
  31. return $this->_csrfToken;
  32. }

验证token

在controller.php里调用request.php里的validateCsrfToken方法

  1. /**
  2. * @inheritdoc
  3. */
  4. public function beforeAction($action)
  5. {
  6. if (parent::beforeAction($action)) {
  7. if ($this->enableCsrfValidation && Yii::$app->getErrorHandler()->exception === null && !Yii::$app->getRequest()->validateCsrfToken()) {
  8. throw new BadRequestHttpException(Yii::t('yii', 'Unable to verify your data submission.'));
  9. }
  10. return true;
  11. }
  12. return false;
  13. }
  14. public function validateCsrfToken($token = null)
  15. {
  16. $method = $this->getMethod();
  17. if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
  18. return true;
  19. }
  20. $trueToken = $this->loadCsrfToken();//如果开启了enableCsrfCookie,CsrfToken就从cookie里取,否者从session里取(更安全)
  21. if ($token !== null) {
  22. return $this->validateCsrfTokenInternal($token, $trueToken);
  23. } else {
  24. return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
  25. || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
  26. }
  27. }

获取客户端传入

  1. $this->getBodyParam($this->csrfParam)

然后是validateCsrfTokenInternal

  1. private function validateCsrfTokenInternal($token, $trueToken)
  2. {
  3. if (!is_string($token)) {
  4. return false;
  5. }
  6. $token = base64_decode(str_replace('.', '+', $token));
  7. $n = StringHelper::byteLength($token);
  8. if ($n <= static::CSRF_MASK_LENGTH) {
  9. return false;
  10. }
  11. $mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
  12. $token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
  13. $token = $this->xorTokens($mask, $token);
  14. return $token === $trueToken;
  15. }

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

  1. token1=token^mask

所以 解密时

  1. token=mask^token1=mask^(token^mask)

3.token缓存的解决方案

当页面整体被缓存后,token也被缓存导致验证失败,一种常见的解决思路是每次提交前重新获取token,这样就可以通过验证了。

附录:

str_pad(),该函数返回 input 被从左端、右端或者同时两端被填充到制定长度后的结果。如果可选的 pad_string 参数没有被指定,input 将被空格字符填充,否则它将被 pad_string 填充到指定长度;

str_shuffle() 函数打乱一个字符串,使用任何一种可能的排序方案。

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

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

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

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

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

yii2的csrf验证原理分析及token缓存解决方案的更多相关文章

  1. yii2 csrf验证原理分析

    知识补充 因为yii2 csrf的验证的加解密 涉及到异或运算 所以需要先补充php里字符串异或运算的相关知识,不需要的可以跳过 ^异或运算不一样返回1 否者返回 0在PHP语言中,经常用来做加密的运 ...

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

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

  3. Django中csrf token验证原理

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

  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. Yii2 Restful API 原理分析

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

  7. yii2表单提交CSRF验证

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

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

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

  9. CSRF漏洞原理浅谈

    CSRF漏洞原理浅谈 By : Mirror王宇阳 E-mail : mirrorwangyuyang@gmail.com 笔者并未深挖过CSRF,内容居多是参考<Web安全深度剖析>.& ...

随机推荐

  1. Spring MVC 使用介绍(九)—— 异常处理

    一.概述 Spring MVC异常处理功能的作用为:捕捉处理器的异常,并映射到相应视图 有4种方式: SimpleMappingExceptionResolver:通过配置的方式实现异常处理,该方式简 ...

  2. [洛谷P4234] 最小差值生成树

    题目类型:\(LCT\)动态维护最小生成树 传送门:>Here< 题意:求一棵生成树,其最大边权减最小边权最小 解题思路 和魔法森林非常像.先对所有边进行排序,每次加边的时候删除环上的最小 ...

  3. 【51NOD1847】奇怪的数学题 min_25筛

    题目描述 记\(sgcd(i,j)\)为\(i,j\)的次大公约数. 给你\(n\),求 \[ \sum_{i=1}^n\sum_{j=1}^n{sgcd(i,j)}^k \] 对\(2^{32}\) ...

  4. Android greenDAO 数据库 简单学习之基本使用

    看网上对greenDAO介绍的不错,今天就动手来试一把,看看好不好使. greenDAO 官方网站:http://greendao-orm.com/ 代码托管地址:https://github.com ...

  5. 多版本python及多版本pip使用

    最近做一些网站的发布程序,要用到python3,所以又安装了python3.   www.qlrx.netwww.393662.comwww.qnpx.netwww.393225.com       ...

  6. 老男孩Python全栈学习 S9 日常作业 003

    1.有变量name = "aleX leNb" 完成如下操作: # 移除 name 变量对应的值两边的空格,并输出处理结果 # 移除name变量左边的"al"并 ...

  7. 安装mysql和xampp遇到问题

    1.mysql的期望地址和配置的地址不一致: 解决方法:修改注册表 在附件命令提示符输入regedit 找[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Se ...

  8. Maven 学习总结 (一)

    一.何为Maven 1.Maven是优秀的构建工具 maven的用途之一是用于构建,他是一个强大的构建工具,能够帮助我们自动化构建过程,从清理.编译.测试到生成报告,再到打包和部署. 他抽象了一个完整 ...

  9. JavaScript 日期和时间基础知识

    前言 学习Date对象之前,首先要先了解关于日期和时间的一些知识.比如,闰年.UTC等等.深入了解这些,有助于更好地理解javascript中的Date对象. 标准时间 一般而言的标准时间是指GMT和 ...

  10. UE4材质特别属生记录

    tangent space normal 切线空间法线 材质默认使用的切线空间法线,可切换为对象空间法线(彩色贴图) Separate Translucency 单独半透明 允许半透明被渲染到一个单独 ...