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);
}
}
<?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>
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。