跨站請求偽造(英語:cross-site request forgery),也被稱為?one-click attack?或者?session riding,通常縮寫為?csrf?或者?xsrf, 是一種挾制用戶在當(dāng)前已登錄的web應(yīng)用程序上執(zhí)行非本意的操作的攻擊方法。
跨站請求攻擊,簡單地說,是攻擊者通過一些技術(shù)手段欺騙用戶的瀏覽器去訪問一個自己曾經(jīng)認(rèn)證過的網(wǎng)站并運(yùn)行一些操作(如發(fā)郵件,發(fā)消息,甚至財(cái)產(chǎn)操作如轉(zhuǎn)賬和購買商品)。 ? ? ? ? ? ? (推薦學(xué)習(xí):yii框架)
由于瀏覽器曾經(jīng)認(rèn)證過,所以被訪問的網(wǎng)站會認(rèn)為是真正的用戶操作而去運(yùn)行。
這利用了web中用戶身份驗(yàn)證的一個漏洞:簡單的身份驗(yàn)證只能保證請求發(fā)自某個用戶的瀏覽器,卻不能保證請求本身是用戶自愿發(fā)出的。
yii2的csrf,這里簡單介紹一下它的驗(yàn)證機(jī)制。
取用于csrf驗(yàn)證的token值;判斷用于csrf的token是否存在,如果不存在則使用generateCsrfToken()生成。
驗(yàn)證webController中的beforeAction()方法中有Yii::$app->getRequest()->validateCsrfToken()判斷,用于驗(yàn)證csrf。?
一般我的認(rèn)識yii2的csrf都是從Yii::$app->request->getCsrfToken()開始;好的,我們就從getCsrfToken()說起。 此方法在yiiwebRequest.php中:
/** ?*?Returns?the?token?used?to?perform?CSRF?validation. ?*?返回用于執(zhí)行CSRF驗(yàn)證的token ?*?This?token?is?a?masked?version?of?[[rawCsrfToken]]?to?prevent?[BREACH?attacks](http://breachattack.com/). ?*?This?token?may?be?passed?along?via?a?hidden?field?of?an?HTML?form?or?an?HTTP?header?value ?*?to?support?CSRF?validation. ?*?@param?boolean?$regenerate?whether?to?regenerate?CSRF?token.?When?this?parameter?is?true,?each?time ?*?this?method?is?called,?a?new?CSRF?token?will?be?generated?and?persisted?(in?session?or?cookie). ?*?@return?string?the?token?used?to?perform?CSRF?validation. ?*/ public?function?getCsrfToken($regenerate?=?false) { ????if?($this->_csrfToken?===?null?||?$regenerate)?{ ????????if?($regenerate?||?($token?=?$this->loadCsrfToken())?===?null)?{????//loadCsrfToken()就是在cookie或者session中獲取token值 ????????????$token?=?$this->generateCsrfToken();????????//如果token為空則調(diào)用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; } /** ?*?Loads?the?CSRF?token?from?cookie?or?session. ?*?@return?string?the?CSRF?token?loaded?from?cookie?or?session.?Null?is?returned?if?the?cookie?or?session ?*?does?not?have?CSRF?token. ?*/ protected?function?loadCsrfToken() { ????if?($this->enableCsrfCookie)?{ ????????return?$this->getCookies()->getValue($this->csrfParam);?????????//cookie中獲取csrf的token? ????}?else?{ ????????return?Yii::$app->getSession()->get($this->csrfParam);??????????//session中獲取csrf的token ????} } /** ?*?Creates?a?cookie?with?a?randomly?generated?CSRF?token. ?*?Initial?values?specified?in?[[csrfCookie]]?will?be?applied?to?the?generated?cookie. ?*?@param?string?$token?the?CSRF?token ?*?@return?Cookie?the?generated?cookie ?*?@see?enableCsrfValidation ?*/ protected?function?createCsrfCookie($token) { ????$options?=?$this->csrfCookie; ????$options['name']?=?$this->csrfParam; ????$options['value']?=?$token; ????return?new?Cookie($options); } /** ?*?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();??????//生成隨機(jī)的安全字符串 ????if?($this->enableCsrfCookie)?{ ????????$cookie?=?$this->createCsrfCookie($token);??????????????????//createCsrfCookie()用于生成csrf的key=>value形式的token ????????Yii::$app->getResponse()->getCookies()->add($cookie);???????//將生成key=>value保存到cookies? ????}?else?{ ????????Yii::$app->getSession()->set($this->csrfParam,?$token);?????//將csrf的token存在session中 ????} ????return?$token; } /** ?*?每次調(diào)用控制器中的方法的時候都會調(diào)用下面的Yii::$app->getRequest()->validateCsrfToken()驗(yàn)證 ?*?@inheritdoc ?*/ 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; ????}?else?{ ????????return?false; ????} } /** ?*?校驗(yàn)方法 ?*?Performs?the?CSRF?validation. ?* ?*?This?method?will?validate?the?user-provided?CSRF?token?by?comparing?it?with?the?one?stored?in?cookie?or?session. ?*?This?method?is?mainly?called?in?[[Controller::beforeAction()]]. ?* ?*?Note?that?the?method?will?NOT?perform?CSRF?validation?if?[[enableCsrfValidation]]?is?false?or?the?HTTP?method ?*?is?among?GET,?HEAD?or?OPTIONS. ?* ?*?@param?string?$token?the?user-provided?CSRF?token?to?be?validated.?If?null,?the?token?will?be?retrieved?from ?*?the?[[csrfParam]]?POST?field?or?HTTP?header. ?*?This?parameter?is?available?since?version?2.0.4. ?*?@return?boolean?whether?CSRF?token?is?valid.?If?[[enableCsrfValidation]]?is?false,?this?method?will?return?true. ?*/ 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);? ????????????//getCsrfTokenFromHeader()這個我也不太理解,還請指點(diǎn)一下 ????} } /** ?*?@return?string?the?CSRF?token?sent?via?[[CSRF_HEADER]]?by?browser.?Null?is?returned?if?no?such?header?is?sent. ?*/ public?function?getCsrfTokenFromHeader() { ????$key?=?'HTTP_'?.?str_replace('-',?'_',?strtoupper(static::CSRF_HEADER)); ????return?isset($_SERVER[$key])???$_SERVER[$key]?:?null; } /** ?*?Validates?CSRF?token ?* ?*?@param?string?$token ?*?@param?string?$trueToken ?*?@return?boolean ?*/ private?function?validateCsrfTokenInternal($token,?$trueToken) { ????$token?=?base64_decode(str_replace('.',?'+',?$token));??????//解碼從客戶端獲取的csrf的token ????$n?=?StringHelper::byteLength($token); ????if?($n?xorTokens($mask,?$token); ????return?$token?===?$trueToken;???????//驗(yàn)證從客戶端獲取的csrf的token和真實(shí)的token是否相等 }