好吧,我花了 3 天时间试图找出这里发生了什么,但我很困惑。
该站点是使用 CI 构建的。 ( http://horizoneslchina.com )
有一段时间,每当有人在中国时,我都会收到 Disallowed Key Characters。
我用以下内容扩展了核心输入类:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Input extends CI_Input {
function _clean_input_keys($str)
{
$config=&get_config('config');
if(!preg_match("/^[".$config['permitted_uri_chars']."]+$/i",rawurlencode($str)))
{
exit('Disallowed Key Characters. '.$str);
}
//CleanUTF-8ifsupported
if(UTF8_ENABLED===TRUE)
{
$str=$this->uni->clean_string($str);
}
return $str;
}
}
/* End of file My_Input.php */
它似乎解决了那个问题,但现在我遇到的情况是,没有人的 session 会保持完整。当中国有人登录并尝试访问任何安全页面时,它会立即终止 session 。
这不会发生在澳大利亚,也不会发生在印度。我能找到的唯一区别是在中文系统上,逗号被放置在 session 名称的前面。 EG ,_ci_session 和上面的函数将其作为之前的问题抛出。
有人对此有任何想法吗?
最佳答案
I also experienced alot of issues on CI sessions and ended up using this library for sessions ever since i am a happy man
<?php
/**
The EckoTools Session Library
@package The EckoTools Session Library
@category Libraries
@author Hartmut König (h.koenig@eckotools.com)
@link http://www.okidoe.de
@version 1.0.2
@copyright Hartmut König 2009
A class to handle sessions by using a mySQL database for session related
data storage providing better security then the default session handler
used by PHP with added protection against Session Hijacking & Fixation
including the flashdata-Feature of CI. It don't use Browser or IP to identify
the user. Instead I generate a fingerprint of different seldom changing data
(@link _generate_fingerprint)
To prevent session hijacking, don't forget to use the {@link regenerate_id}
method whenever you do a privilege change in your application
--
-- MYSQL: Table structure for table `ci_sessions`
--
CREATE TABLE `ci_sessions` (
`session_id` varchar(32) NOT NULL default '',
`fingerprint` varchar(32) NOT NULL default '',
`session_data` blob NOT NULL,
`session_expire` int(11) NOT NULL default '0',
PRIMARY KEY (`session_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
This class is an adaptation between the original CI Sessions, Native Sessions
and my own coding
*/
error_reporting(E_ALL);
class CI_Session
{
/**
* Constructor of class
*
* Initializes the class and starts a new session
*
* There is no need to call start_session() after instantiating this class
*
* $gc_maxlifetime (optional) the number of seconds after which data will be seen as 'garbage' and
* cleaned up on the next run of the gc (garbage collection) routine
*
* Default is specified in php.ini file
*
* $gc_probability (optional) used in conjunction with gc_divisor, is used to manage probability that
* the gc routine is started. the probability is expressed by the formula
*
* probability = $gc_probability / $gc_divisor
*
* So if $gc_probability is 1 and $gc_divisor is 100 means that there is
* a 1% chance the the gc routine will be called on each request
*
* Default is specified in php.ini file
*
* $gc_divisor (optional) used in conjunction with gc_probability, is used to manage probability
* that the gc routine is started. the probability is expressed by the formula
*
* probability = $gc_probability / $gc_divisor
*
* So if $gc_probability is 1 and $gc_divisor is 100 means that there is
* a 1% chance the the gc routine will be called on each request
*
* Default is specified in php.ini file
*
* $security_code (optional) the value of this argument is appended to the fingerprint before
* creating the md5 hash out of it. this way we'll try to prevent fingerprint
* spoofing
*
* Default is 'LeouOeEkKpvSnD-YCHd5ogt3y'
*
* $table_name (optional) You can change the name of that table by setting this property
*
* Default is 'ci_sessions'
*
* @return void
*/
function CI_Session( $security_code="LeouOeEkKpvSnD-YCHd5ogt3y",$table_name="ci_sessions" )
{
//-- CI Config
$this->CI = & get_instance();
$this->flashdata_key = 'flash'; // prefix for "flash" variables (eg. flash:new:message)
$table_name = $this->CI->config->item('sess_table_name');
$gc_maxlifetime = $this->CI->config->item('sess_expiration');
$gc_probability = $this->CI->config->item('sess_gc_probability');
$gc_divisor = $this->CI->config->item('sess_gc_divisor');
$sess_name = $this->CI->config->item('sess_cookie_name');
// if $gc_maxlifetime is specified and is an integer number
(!empty($gc_maxlifetime) && is_integer($gc_maxlifetime))
? @ini_set('session.gc_maxlifetime', $gc_maxlifetime)
: false;
// if $gc_probability is specified and is an integer number
(!empty($gc_probability) && is_integer($gc_probability))
? @ini_set('session.gc_probability', $gc_probability)
: false;
// if $gc_divisor is specified and is an integer number
(!empty($gc_divisor) && is_integer($gc_divisor))
? @ini_set('session.gc_divisor', $gc_divisor)
: false;
(!empty($sess_name))
? @ini_set('session.name', $sess_name)
: false;
// get session lifetime
$this->sessionLifetime = ini_get("session.gc_maxlifetime");
// we'll use this later in order to prevent fingerprint spoofing
$this->securityCode = $security_code;
$this->tableName = $table_name;
// register the new handler
session_set_save_handler(
array(&$this, '_open'),
array(&$this, '_close'),
array(&$this, '_read'),
array(&$this, '_write'),
array(&$this, '_destroy'),
array(&$this, '_gc')
);
register_shutdown_function('session_write_close');
// start the session
session_start();
// Delete 'old' flashdata (from last request)
$this->_flashdata_sweep();
// Mark all new flashdata as old (data will be deleted before next request)
$this->_flashdata_mark();
}
/**
* Reads given session attribute value
*
* @return integer sessionvalue
*/
function userdata($item)
{
//added for backward-compatibility
if($item == 'session_id')
{
return session_id();
}
if(isset($_SESSION[$item]))
{
return($_SESSION[$item]);
}
return(false);
}
/**
* Fetch all session data
*
* @access public
* @return mixed
*/
function all_userdata()
{
return ( ! isset($_SESSION)) ? FALSE : $_SESSION;
}
/**
* Sets session attributes to the given values
*
* @return void
*/
function set_userdata($newdata = array(), $newval = '')
{
(is_string($newdata))
? $newdata = array($newdata => $newval)
: false;
if(count($newdata) > 0)
{
foreach($newdata as $key => $val)
{
$_SESSION[$key] = $val;
}
}
}
/**
* Erases given session attributes
*
* @return void
*/
function unset_userdata($newdata = array())
{
(is_string($newdata))
? $newdata = array($newdata => '')
: false;
if(count($newdata) > 0)
{
foreach ($newdata as $key => $val)
{
unset($_SESSION[$key]);
}
}
}
/**
* Deletes all data related to the session
* @return void
*/
function sess_destroy()
{
$this->regenerate_id();
session_unset();
session_destroy();
}
/**
* Regenerates the session id.
*
* <b>Call this method whenever you do a privilege change!</b>
*
* @return void
*/
function regenerate_id()
{
// saves the old session's id
$oldSessionID = session_id();
// regenerates the id
// this function will create a new session, with a new id and containing the data from the old session
// but will not delete the old session
session_regenerate_id();
// because the session_regenerate_id() function does not delete the old session,
// we have to delete it manually
$this->_destroy($oldSessionID);
}
/**
* Add or change flashdata, only available
* until the next request
*
* @access public
* @param mixed
* @param string
* @return void
*/
function set_flashdata($newdata = array(), $newval = '')
{
if (is_string($newdata))
{
$newdata = array($newdata => $newval);
}
if (count($newdata) > 0)
{
foreach ($newdata as $key => $val)
{
$flashdata_key = $this->flashdata_key.':new:'.$key;
$this->set_userdata($flashdata_key, $val);
}
}
}
// ------------------------------------------------------------------------
/**
* Keeps existing flashdata available to next request.
*
* @access public
* @param string
* @return void
*/
function keep_flashdata($key)
{
// 'old' flashdata gets removed. Here we mark all
// flashdata as 'new' to preserve it from _flashdata_sweep()
// Note the function will return FALSE if the $key
// provided cannot be found
$old_flashdata_key = $this->flashdata_key.':old:'.$key;
$value = $this->userdata($old_flashdata_key);
$new_flashdata_key = $this->flashdata_key.':new:'.$key;
$this->set_userdata($new_flashdata_key, $value);
}
// ------------------------------------------------------------------------
/**
* Fetch a specific flashdata item from the session array
*
* @access public
* @param string
* @return string
*/
function flashdata($key)
{
$flashdata_key = $this->flashdata_key.':old:'.$key;
return $this->userdata($flashdata_key);
}
// ------------------------------------------------------------------------
/**
* Identifies flashdata as 'old' for removal
* when _flashdata_sweep() runs.
*
* @access private
* @return void
*/
function _flashdata_mark()
{
$userdata = $this->all_userdata();
foreach ($userdata as $name => $value)
{
$parts = explode(':new:', $name);
if (is_array($parts) && count($parts) === 2)
{
$new_name = $this->flashdata_key.':old:'.$parts[1];
$this->set_userdata($new_name, $value);
$this->unset_userdata($name);
}
}
}
// ------------------------------------------------------------------------
/**
* Removes all flashdata marked as 'old'
*
* @access private
* @return void
*/
function _flashdata_sweep()
{
$userdata = $this->all_userdata();
foreach($userdata as $key => $value)
{
if (strpos($key, ':old:'))
{
$this->unset_userdata($key);
}
}
}
/**
* Get the number of online users
*
* @return integer number of users currently online
*/
function get_users_online()
{
// counts the rows from the database
$query = $this->CI->db->query("SELECT COUNT(session_id) as count FROM ".$this->tableName);
$result = $query->row();
// return the number of found rows
return $result->count;
}
/**
* Generates key as protection against Session Hijacking & Fixation.
* @access private
* @return string
*/
function _generate_fingerprint()
{
//-- We don't use the ip-adress, because this is a subject to change in most cases (proxies etc.)
$list = array('HTTP_ACCEPT_CHARSET',
'HTTP_ACCEPT_ENCODING',
'HTTP_ACCEPT_LANGUAGE',
'HTTP_USER_AGENT');
$key = array($this->securityCode);
foreach($list as $item)
{
$key[] = $this->CI->input->server($item);
}
return md5(implode("\0", $key));
}
/**
* Custom open() function
*
* @access private
*/
function _open($save_path, $session_name)
{
return(true);
}
/**
* Custom close() function
*
* @access private
*/
function _close()
{
return(true);
}
/**
* Custom read() function
*
* @access private
*/
function _read($session_id)
{
// reads session data associated with the session id
// but only
// - if the fingerprint is the same as the one who had previously written to this session AND
// - if session has not expired
$result = $this->CI->db->query("SELECT session_data ".
"FROM ".$this->tableName." ".
"WHERE session_id = ".$this->CI->db->escape($session_id)." ".
"AND fingerprint = ".$this->CI->db->escape($this->_generate_fingerprint())." ".
"AND session_expire > '".time()."' LIMIT 1");
// if anything was found
if($result->num_rows() > 0)
{
// return found data
$fields = $result->row();
// Unserialization - PHP handles this automatically
return $fields->session_data;
}
// if there was an error return an empty string - this HAS to be an empty string
return("");
}
/**
* Custom write() function
*
* @access private
*/
function _write($session_id, $session_data)
{
// insert OR update session's data - this is how it works:
// first it tries to insert a new row in the database BUT if session_id is already in the database then just
// update session_data and session_expire for that specific session_id
// read more here http://dev.mysql.com/doc/refman/4.1/en/insert-on-duplicate.html
$result = $this->CI->db->query(
"INSERT INTO ".$this->tableName." (".
"session_id,".
"fingerprint,".
"session_data,".
"session_expire".
") VALUES (".
$this->CI->db->escape($session_id).",".
$this->CI->db->escape($this->_generate_fingerprint()).",".
$this->CI->db->escape($session_data).",".
$this->CI->db->escape(time() + $this->sessionLifetime).
")".
"ON DUPLICATE KEY UPDATE ".
"session_data = ".$this->CI->db->escape($session_data).",".
"session_expire = ".$this->CI->db->escape(time() + $this->sessionLifetime));
// note that after this type of queries, mysql_affected_rows() returns
// - 1 if the row was inserted
// - 2 if the row was updated
switch($this->CI->db->affected_rows())
{
// if the row was inserted
case 1:
return("");
break;
// if the row was updated
case 2:
return(true);
break;
// if something went wrong, return false
default:
return(false);
break;
}
}
/**
* Custom destroy() function
*
* @access private
*/
function _destroy($session_id)
{
// deletes the current session id from the database
$result = $this->CI->db->query("DELETE FROM ".$this->tableName." ".
"WHERE session_id = ".$this->CI->db->escape($session_id));
// if anything happened
if($this->CI->db->affected_rows())
{
return(true);
}
// if something went wrong, return false
return(false);
}
/**
* Custom gc() function (garbage collector)
*
* @access private
*/
function _gc($maxlifetime)
{
// it deletes expired sessions from database
$result = $this->CI->db->query("DELETE FROM ".$this->tableName." ".
"WHERE session_expire < ".$this->CI->db->escape(time() - $maxlifetime));
}
}
?>
关于php - CodeIgniter session 仅在中国失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22245852/
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf
我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束
我正在尝试在配备ARMv7处理器的SynologyDS215j上安装ruby2.2.4或2.3.0。我用了optware-ng安装gcc、make、openssl、openssl-dev和zlib。我根据README中的说明安装了rbenv(版本1.0.0-19-g29b4da7)和ruby-build插件。.这些是随optware-ng安装的软件包及其版本binutils-2.25.1-1gcc-5.3.0-6gconv-modules-2.21-3glibc-opt-2.21-4libc-dev-2.21-1libgmp-6.0.0a-1libmpc-1.0.2-1libm
一段时间以来,我一直在使用open_uri下拉ftp路径作为数据源,但突然发现我几乎连续不断地收到“530抱歉,允许的最大客户端数(95)已经连接。”我不确定我的代码是否有问题,或者是否是其他人在访问服务器,不幸的是,我无法真正确定谁有问题。本质上,我正在读取FTPURI:defself.read_uri(uri)beginuri=open(uri).readuri=="Error"?nil:urirescueOpenURI::HTTPErrornilendend我猜我需要在这里添加一些额外的错误处理代码...我想确保我采取一切预防措施来关闭所有连接,这样我的连接就不是问题所在,但是我
我在思考流量控制的最佳实践。我应该走哪条路?1)不要检查任何东西并让程序失败(更清晰的代码,自然的错误消息):defself.fetch(feed_id)feed=Feed.find(feed_id)feed.fetchend2)通过返回nil静默失败(但是,“CleanCode”说,你永远不应该返回null):defself.fetch(feed_id)returnunlessfeed_idfeed=Feed.find(feed_id)returnunlessfeedfeed.fetchend3)抛出异常(因为不按id查找feed是异常的):defself.fetch(feed_id
我正在为毕业设计开发GEM,TravisCI构建不断失败。这是我在Travis上的链接:https://travis-ci.org/ricardobond/perpetuus/builds/8709218构建错误是:$bundleexecrakerakeaborted!Don'tknowhowtobuildtask'default'/home/travis/.rvm/gems/ruby-1.9.3-p448/bin/ruby_noexec_wrapper:14:in`eval'/home/travis/.rvm/gems/ruby-1.9.3-p448/bin/ruby_noexec_
运行:ruby1.9.3p0和Rails3.2.1尝试使用rspec但当我尝试将其安装到我的应用程序中时出现以下错误:/Users/Si/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.2.1/lib/rails/railtie/configuration.rb:85:in`method_missing':undefinedmethod`generators'for#(NoMethodError)from/Users/Si/.rvm/gems/ruby-1.9.3-p0/gems/rspec-rails-2.0.0.beta.18/lib/rspec-r
我有一个类unzipper.rb,它使用Rubyzip解压文件。在我的本地环境中,我可以成功解压缩文件,而无需使用require'zip'明确包含依赖项但是在Heroku上,我得到一个NameError(uninitializedconstantUnzipper::Zip)我只能通过使用明确的require来解决问题:为什么这在Heroku环境中是必需的,但在本地主机上却不是?我的印象是Rails自动需要所有gem。app/services/unzipper.rbrequire'zip'#OnlyrequiredforHeroku.Workslocallywithout!class
我正在运行这样的代码:ifvalid_from>Date.today当我运行它时,我得到一个错误提示comparisonofDatewithnilfailed我假设它正在发生,因为在某些情况下valid_from是nil。有没有办法避免出现此错误? 最佳答案 你可以这样做:ifvalid_fromandvalid_from>Date.today...end这将在第一个子句上短路,因为valid_from为nil,因此为false。 关于ruby-Date与nil的比较失败-ruby,我们