Yii2 CSRF

一、CSRF

即Cross-site request forgery跨站請(qǐng)求偽造琢唾,是指有人冒充你的身份進(jìn)行一些惡意操作兴蒸。
比如你登錄了網(wǎng)站A,網(wǎng)站A在你的電腦設(shè)置了cookie用以標(biāo)識(shí)身份和狀態(tài)裳瘪,然后你又訪問了網(wǎng)站B,這時(shí)候網(wǎng)站B就可以冒充你的身份在A網(wǎng)站進(jìn)行操作土浸,因?yàn)榫W(wǎng)站B在請(qǐng)求網(wǎng)站A時(shí),瀏覽器會(huì)自動(dòng)發(fā)送之前設(shè)置的cookie信息彭羹,讓網(wǎng)站A誤認(rèn)為仍然是你在進(jìn)行操作栅迄。
對(duì)于csrf的防范,一般都會(huì)放在服務(wù)器端進(jìn)行皆怕,那么我們來看下Yii2中是如何進(jìn)行防范的。

二西篓、Yii2 CSRF

首先說明一下愈腾,我安裝的是Yii2高級(jí)模版。

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方法首先會(huì)用loadCsrfToken方法嘗試加載已存在的token岂津,如果沒有則用generateCsrfToken方法再生成一個(gè)虱黄,并經(jīng)過后續(xù)處理,得到最終的前臺(tái)請(qǐng)求時(shí)攜帶的csrf token吮成。

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

loadCsrfToken方法會(huì)嘗試從cookie或session中加載已經(jīng)存在的token橱乱,enableCsrfCookie默認(rèn)為true,所以一般會(huì)從cookie中獲取

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

    return $this->_cookies;
}

這里又調(diào)用了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;
    }

這里就是解析驗(yàn)證$_COOKIE中的數(shù)據(jù)粱甫。

cookies設(shè)置

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對(duì)cookie進(jìn)行一系列處理泳叠,主要是為了獲取的時(shí)候進(jìn)行驗(yàn)證,防止cookie被篡改茶宵。

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

這里的getCookies方法跟request中的不同危纫,并不會(huì)從$_COOKIE中獲取,_cookies屬性在request中的generateCsrfToken方法中有進(jìn)行設(shè)置

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驗(yàn)證

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

這里先驗(yàn)證一下請(qǐng)求方式,接著獲取cookie中的token种蝶,然后用validateCsrfTokenInternal方法進(jìn)行對(duì)比

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

解析請(qǐng)求攜帶的csrf token 進(jìn)行對(duì)比并返回結(jié)果契耿。

三、總結(jié)

Yii2的做法就是先生成一個(gè)隨機(jī)token螃征,存入cookie中搪桂,同時(shí)在請(qǐng)求中攜帶隨機(jī)生成的csrf token,也是基于之前的隨機(jī)token而生成的盯滚,驗(yàn)證的時(shí)候?qū)ookie和csrf token進(jìn)行解析踢械,得到隨機(jī)token進(jìn)行對(duì)比,從而判斷請(qǐng)求是否合法淌山。
最后裸燎,本文只是對(duì)大概的流程進(jìn)行了分析,具體的細(xì)節(jié)還請(qǐng)查看源碼泼疑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末德绿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子退渗,更是在濱河造成了極大的恐慌移稳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件会油,死亡現(xiàn)場(chǎng)離奇詭異个粱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翻翩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門都许,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嫂冻,你說我怎么就攤上這事胶征。” “怎么了桨仿?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵睛低,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我服傍,道長(zhǎng)钱雷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任吹零,我火速辦了婚禮罩抗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘪校。我一直安慰自己澄暮,他們只是感情好名段,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泣懊,像睡著了一般伸辟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馍刮,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天信夫,我揣著相機(jī)與錄音,去河邊找鬼卡啰。 笑死静稻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匈辱。 我是一名探鬼主播振湾,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼亡脸!你這毒婦竟也來了押搪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤浅碾,失蹤者是張志新(化名)和其女友劉穎大州,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垂谢,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厦画,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滥朱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徙邻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鹃栽,到底是詐尸還是另有隱情,我是刑警寧澤民鼓,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站丰嘉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嚷缭。R本人自食惡果不足惜耍贾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望路幸。 院中可真熱鬧,春花似錦简肴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辫狼。三九已至,卻和暖如春膨处,著一層夾襖步出監(jiān)牢的瞬間见秤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工灵迫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秦叛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓瀑粥,卻偏偏與公主長(zhǎng)得像挣跋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狞换,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • http://www.91ri.org/tag/fuzz-bug 通常情況下避咆,有三種方法被廣泛用來防御CSRF攻擊...
    jdyzm閱讀 4,176評(píng)論 0 5
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)修噪,斷路器查库,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • CSRF 攻擊的應(yīng)對(duì)之道web安全之token和CSRF攻擊CSRF Token 的設(shè)計(jì)是否有其必要性? CSRF...
    拾壹北閱讀 409評(píng)論 0 1
  • 轉(zhuǎn)載地址:http://www.phpddt.com/reprint/csrf.htmlCSRF概念:CSRF跨站...
    matianhe閱讀 982評(píng)論 0 104
  • CSRF概念:CSRF跨站點(diǎn)請(qǐng)求偽造(Cross—Site Request Forgery)黄琼,跟XSS攻擊一樣樊销,存...
    raincoco閱讀 831評(píng)論 0 1