菜单

Yii2 CSRF

2019年2月1日 - 金沙编程资讯

率先通过安全组件Security获取一个32位的妄动字符串,并存入cookie或session,那是原生的token.

三、总结

Yii2的做法就是读书人成一个无限制token,存入cookie中,同时在呼吁中率领随机变化的csrf
token,也是基于从前的擅自token而变化的,验证的时候对cookie和csrf
token举办分析,得到随机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 CSRF

首先说圣元下,我设置的是Yii2高级模版。

加密时用的是 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
注目的在于加密时

一、CSRF

即Cross-site request
forgery跨站请求伪造,是指有人伪造你的身价进行局地恶心操作。
诸如您登录了网站A,网站A在你的处理器安装了cookie用以标识身份和景观,然后你又走访了网站B,那时候网站B就足以伪造你的身份在A网站举办操作,因为网站B在伸手网站A时,浏览器会自动发送在此之前安装的cookie信息,让网站A误以为仍旧是您在进展操作。
对于csrf的防护,一般都会放在服务器端举办,那么大家来看下Yii2中是什么样开展预防的。

二、Yii2 CSRF

先是说Bellamy(Bellamy)下,我设置的是Yii2高等模版。

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;
}

在控制器里将enableCsrfValidation为true,则控制器内享有操作都会开启验证,平日做法是将enableCsrfValidation为false,而将部分机智操作设为true,开启局地验证。

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 进行对照并回到结果。

一、CSRF

即Cross-site request
forgery跨站请求伪造,是指有人冒领你的身份展开一些黑心操作。
比如说你登录了网站A,网站A在您的微处理器安装了cookie用以标识身份和处境,然后您又走访了网站B,那时候网站B就可以以假乱真你的地位在A网站开展操作,因为网站B在央浼网站A时,浏览器会活动发送此前安装的cookie音讯,让网站A误以为仍然是你在举办操作。
对于csrf的防护,一般都会放在服务器端进行,那么咱们来看下Yii2中是何等进行防备的。

三、总结

Yii2的做法就是读书人成一个随机token,存入cookie中,同时在哀告中教导随机变化的csrf
token,也是根据以前的擅自token而转变的,验证的时候对cookie和csrf
token举行剖析,得到随机token举行相比,从而判断请求是不是合法。
最后,本文只是对大约的流水线举行了解析,具体的细节还请查看源码。

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

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;
}

三、总结

Yii2的做法就是学子成一个随机token,存入cookie中,同时在哀求中带走随机生成的csrf
token,也是依据以前的任意token而变化的,验证的时候对cookie和csrf
token举行辨析,获得随机token进行比较,从而判断请求是或不是合法。
最后,本文只是对大致的流程展开通晓析,具体的底细还请查看源码。

一、CSRF

即Cross-site request
forgery跨站请求伪造,是指有人伪造你的身份展开局地恶心操作。
譬如你登录了网站A,网站A在您的微机安装了cookie用以标识身份和气象,然后您又走访了网站B,那时候网站B就足以以假乱真你的地位在A网站开展操作,因为网站B在呼吁网站A时,浏览器会自行发送此前安装的cookie新闻,让网站A误以为依旧是您在拓展操作。
对此csrf的防护,一般都会放在服务器端进行,那么大家来看下Yii2中是何等举行预防的。

/**
 * Generates  an unmasked random token used to perform CSRF validation.
 * @return string the random token for CSRF validation.
 */
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 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;
    }

那里就是分析验证$_经理KIE中的数据。

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中的数据。

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;
    }

此处就是分析验证$_老董KIE中的数据。

1.CSRF描述

二、Yii2 CSRF

第一表达一下,我设置的是Yii2尖端模版。

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 举行比较并赶回结果。

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

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

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

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

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图