博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Yii2 CSRF
阅读量:5338 次
发布时间:2019-06-15

本文共 6007 字,大约阅读时间需要 20 分钟。

一、CSRF

即Cross-site request forgery跨站请求伪造,是指有人冒充你的身份进行一些恶意操作。

比如你登录了网站A,网站A在你的电脑设置了cookie用以标识身份和状态,然后你又访问了网站B,这时候网站B就可以冒充你的身份在A网站进行操作,因为网站B在请求网站A时,浏览器会自动发送之前设置的cookie信息,让网站A误认为仍然是你在进行操作。
对于csrf的防范,一般都会放在服务器端进行,那么我们来看下Yii2中是如何进行防范的。

二、Yii2 CSRF

首先说明一下,我安装的是Yii2高级模版。

csrf token生成

vendor\yiisoft\yii2\web\Request.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;}

getCsrfToken方法首先会用loadCsrfToken方法尝试加载已存在的token,如果没有则用generateCsrfToken方法再生成一个,并经过后续处理,得到最终的前台请求时携带的csrf token。

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

loadCsrfToken方法会尝试从cookie或session中加载已经存在的token,enableCsrfCookie默认为true,所以一般会从cookie中获取

public function getCookies(){    if ($this->_cookies === null) {        $this->_cookies = new CookieCollection($this->loadCookies(), [            'readOnly' => true,        ]);    }    return $this->_cookies;}

这里又调用了loadCookies方法

protected function loadCookies()    {        $cookies = [];        if ($this->enableCookieValidation) {            if ($this->cookieValidationKey == '') {                throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');            }            foreach ($_COOKIE as $name => $value) {                if (!is_string($value)) {                    continue;                }                $data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);                if ($data === false) {                    continue;                }                $data = @unserialize($data);                if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {                    $cookies[$name] = new Cookie([                        'name' => $name,                        'value' => $data[1],                        'expire' => null,                    ]);                }            }        } else {            foreach ($_COOKIE as $name => $value) {                $cookies[$name] = new Cookie([                    'name' => $name,                    'value' => $value,                    'expire' => null,                ]);            }        }        return $cookies;    }

这里就是解析验证$_COOKIE中的数据。

cookies设置

vendor\yiisoft\yii2\web\Response.php

protected function sendCookies(){    if ($this->_cookies === null) {        return;    }    $request = Yii::$app->getRequest();    if ($request->enableCookieValidation) {        if ($request->cookieValidationKey == '') {            throw new InvalidConfigException(get_class($request) . '::cookieValidationKey must be configured with a secret key.');        }        $validationKey = $request->cookieValidationKey;    }    foreach ($this->getCookies() as $cookie) {        $value = $cookie->value;        if ($cookie->expire != 1  && isset($validationKey)) {            $value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey);        }        setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);    }}

sendCookies方法利用cookieValidationKey对cookie进行一系列处理,主要是为了获取的时候进行验证,防止cookie被篡改。

public function getCookies(){    if ($this->_cookies === null) {        $this->_cookies = new CookieCollection;    }    return $this->_cookies;}

这里的getCookies方法跟request中的不同,并不会从$_COOKIE中获取,_cookies属性在request中的generateCsrfToken方法中有进行设置

protected function generateCsrfToken(){    $token = Yii::$app->getSecurity()->generateRandomString();    if ($this->enableCsrfCookie) {        $cookie = $this->createCsrfCookie($token);        Yii::$app->getResponse()->getCookies()->add($cookie);    } else {        Yii::$app->getSession()->set($this->csrfParam, $token);    }    return $token;}

csrf验证

vendor\yiisoft\yii2\web\Request.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);    }}

这里先验证一下请求方式,接着获取cookie中的token,然后用validateCsrfTokenInternal方法进行对比

private function validateCsrfTokenInternal($token, $trueToken){    if (!is_string($token)) {        return false;    }    $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 = $this->xorTokens($mask, $token);    return $token === $trueToken;}

解析请求携带的csrf token 进行对比并返回结果。

三、总结

Yii2的做法就是先生成一个随机token,存入cookie中,同时在请求中携带随机生成的csrf token,也是基于之前的随机token而生成的,验证的时候对cookie和csrf token进行解析,得到随机token进行对比,从而判断请求是否合法。

最后,本文只是对大概的流程进行了分析,具体的细节还请查看源码。

转载于:https://www.cnblogs.com/Yanger90/p/5799739.html

你可能感兴趣的文章
delphi 内嵌汇编例子
查看>>
【luogu P2298 Mzc和男家丁的游戏】 题解
查看>>
前端笔记-bom
查看>>
MATLAB作图方法与技巧(一)
查看>>
上海淮海中路上苹果旗舰店门口欲砸一台IMAC电脑维权
查看>>
Google透露Android Market恶意程序扫描服务
查看>>
给mysql数据库字段值拼接前缀或后缀。 concat()函数
查看>>
迷宫问题
查看>>
【FZSZ2017暑假提高组Day9】猜数游戏(number)
查看>>
泛型子类_属性类型_重写方法类型
查看>>
eclipse-将同一个文件分屏显示
查看>>
对闭包的理解
查看>>
练习10-1 使用递归函数计算1到n之和(10 分
查看>>
Oracle MySQL yaSSL 不明细节缓冲区溢出漏洞2
查看>>
windows编程ASCII问题
查看>>
.net webService代理类
查看>>
Code Snippet
查看>>
Node.js Express项目搭建
查看>>
zoj 1232 Adventure of Super Mario
查看>>
1201 网页基础--JavaScript(DOM)
查看>>