CI的CSRF是有缺陷的。
只要同时开俩个不同的涉及csrf的页面,
http://host/csrf1
http://host/csrf2
就会发现页面直接互相影响(问题1)。
即使同一页面也涉及这样的问题。
http://host/csrf1
http://host/csrf1
也会发现这样的问题(问题2)。
先解决简单问题2
只需要将配置 csrf_regenerate 设置为 false; 即是cookie过期或者被清掉。
$config['csrf_regenerate'] = FALSE;
解决复杂问题1:
一. 将system/core/Input 中的 验证代码过滤。
// CSRF Protection check if ($this->_enable_csrf === TRUE && ! is_cli()) { //$this->security->csrf_verify(); }
二. 改造Security类
class CI_Security { /** * Class constructor * * @return void */ public function __construct() { $this->charset = strtoupper(config_item('charset')); log_message('info', 'Security Class Initialized'); } public function start_csrf($class, $function) { // Is CSRF protection enabled? if (config_item('csrf_protection')) { if(!$class || !$function){ return ; } $this->_csrf_cookie_name = md5($class.'_'.$function); // CSRF config foreach (array( 'csrf_expire', 'csrf_token_name', //'csrf_cookie_name', ) as $key) { if (NULL !== ($val = config_item($key))) { $this->{'_'.$key} = $val; } } // Append application specific cookie prefix if ($cookie_prefix = config_item('cookie_prefix')) { //$this->_csrf_cookie_name = $cookie_prefix.$this->_csrf_cookie_name; } // Set the CSRF hash $this->_csrf_set_hash(); $this->csrf_set_cookie(); } } // -------------------------------------------------------------------- /** * CSRF Verify * * @return CI_Security */ public function csrf_verify($class, $function) { if (!config_item('csrf_protection')){ return ; } if(!$class || !$function){ return ; } $this->_csrf_cookie_name = md5($class.'_'.$function); // CSRF config foreach (array( 'csrf_expire', 'csrf_token_name', //'csrf_cookie_name', ) as $key) { if (NULL !== ($val = config_item($key))) { $this->{'_'.$key} = $val; } } // If it's not a POST request we will set the CSRF cookie if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') { //return $this->csrf_set_cookie(); $this->csrf_show_error(); } // Check if URI has been whitelisted from CSRF checks if ($exclude_uris = config_item('csrf_exclude_uris')) { $uri = load_class('URI', 'core'); foreach ($exclude_uris as $excluded) { if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string())) { return $this; } } } // Check CSRF token validity, but don't error on mismatch just yet - we'll want to regenerate $valid = isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]) && hash_equals($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]); // We kill this since we're done and we don't want to pollute the _POST array unset($_POST[$this->_csrf_token_name]); // Regenerate on every submission? if (config_item('csrf_regenerate')) { // Nothing should last forever unset($_COOKIE[$this->_csrf_cookie_name]); $this->_csrf_hash = NULL; } $this->_csrf_set_hash(); $this->csrf_set_cookie(); if ($valid !== TRUE) { $this->csrf_show_error(); } log_message('info', 'CSRF token verified'); return $this; } // -------------------------------------------------------------------- /** * CSRF Set Cookie * * @codeCoverageIgnore * @return CI_Security */ public function csrf_set_cookie() { if (!config_item('csrf_protection')){ return ; } $expire = time() + $this->_csrf_expire; $secure_cookie = (bool) config_item('cookie_secure'); if ($secure_cookie && ! is_https()) { return FALSE; } setcookie( $this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie, config_item('cookie_httponly') ); log_message('info', 'CSRF cookie sent'); return $this; } // -------------------------------------------------------------------- /** * Set CSRF Hash and Cookie * * @return string */ protected function _csrf_set_hash() { if (!config_item('csrf_protection')){ return ; } if ($this->_csrf_hash === NULL) { // If the cookie exists we will use its value. // We don't necessarily want to regenerate it with // each page load since a page could contain embedded // sub-pages causing this feature to fail if (isset($_COOKIE[$this->_csrf_cookie_name]) && is_string($_COOKIE[$this->_csrf_cookie_name]) && preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1) { return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; } $rand = $this->get_random_bytes(16); $this->_csrf_hash = ($rand === FALSE) ? md5(uniqid(mt_rand(), TRUE)) : bin2hex($rand); } return $this->_csrf_hash; } }
三.使用实例
controller
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welcome extends CI_Controller { public function csrf_test1() { if($_POST){ $this->security->csrf_verify(__CLASS__, __FUNCTION__); var_dump($_POST); exit; } $this->security->start_csrf(__CLASS__, __FUNCTION__); $csrf = array( 'name' => $this->security->get_csrf_token_name(), 'hash' => $this->security->get_csrf_hash() ); $data['csrf'] = $csrf; $this->load->view('csrf1', $data); } }
view
<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Welcome to CodeIgniter</title> <style type="text/css"> ::selection { background-color: #E13300; color: white; } ::-moz-selection { background-color: #E13300; color: white; } body { background-color: #fff; margin: 40px; font: 13px/20px normal Helvetica, Arial, sans-serif; color: #4F5155; } a { color: #003399; background-color: transparent; font-weight: normal; } h2 { color: #444; background-color: transparent; border-bottom: 1px solid #D0D0D0; font-size: 19px; font-weight: normal; margin: 0 0 14px 0; padding: 14px 15px 10px 15px; } code { font-family: Consolas, Monaco, Courier New, Courier, monospace; font-size: 12px; background-color: #f9f9f9; border: 1px solid #D0D0D0; color: #002166; display: block; margin: 14px 0 14px 0; padding: 12px 10px 12px 10px; } #body { margin: 0 15px 0 15px; } p.footer { text-align: right; font-size: 11px; border-top: 1px solid #D0D0D0; line-height: 32px; padding: 0 10px 0 10px; margin: 20px 0 0 0; } #container { margin: 10px; border: 1px solid #D0D0D0; box-shadow: 0 0 8px #D0D0D0; } </style> </head> <body> <div id="container"> <h2>Welcome to CodeIgniter!</h2> <div id="body"> <form method="post" action="/welcome/csrf_test1"> <table> <tbody> <tr> <td>用户名:</td> <td colspan="3"><input type="text" id="user_name" class="text" name="username"></td> </tr> <tr> <td>密 码:</td> <td class="width260"><input type="password" class="text" name="password"></td> <td colspan="2"> </td> </tr> <tr> <th colspan="4"> <input class="btnenter" type="submit"> <input type="hidden" name="<?=$csrf['name'];?>" value="<?=$csrf['hash'];?>" /> </tr> </tbody> </table> </form> </div> </div> </body> </html>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。