原文:http://www.nowamagic.net/librarys/veda/detail/1721
在 CodeIgniter 做的网站里,想输入一段代码:
1 | $var = sprintf( "%04d" , 2); |
但是发现入库后,代码变成了
1 | $var = sprintf( "d" , 2); |
在网上环境,本地环境都测试过,最终确认是 CodeIgniter 系统的问题。下面谈一下问题解决的过程与思维方法:
1. 是 config.php 的 permitted_uri_chars 吗?
1 | $config [ 'permitted_uri_chars' ] = 'a-z 0-9~%.:_\-' ; |
在 stackoverflow 上找到几个差不多的问题,有答案说改 config.php 的 permitted_uri_chars 就行了。
Ahem... after looking at your sample string again. Here is why you get "The URI you submitted has disallowed characters
".
Short explanation: Add the ampersand & to the allowed characters list
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_+&-';
试过了,没效果,于是就查找应用了 $config['permitted_uri_chars'] 的代码。
2. 是 core/Input.php 的 _clean_input_keys() 函数问题吗?
01 | function _clean_input_keys( $str ) |
03 | $config = &get_config( 'config' ); |
04 | if ( ! preg_match( "/^[" . $config [ 'permitted_uri_chars' ]. "]+$/i" , rawurlencode( $str ))) |
06 | exit ( 'Disallowed Key Characters.' ); |
10 | if (UTF8_ENABLED === TRUE) |
12 | $str = $this ->uni->clean_string( $str ); |
这个函数使用了 $config['permitted_uri_chars'] 直接过滤 post 过来的数据,很大原因就是元凶了。我把它单独出来,经过测试发现,post $var = sprintf("%04d", 2); 过来,结果还是 $var = sprintf("%04d", 2); ,%04并未被过滤,看来还得细细地找。
3. 是 xss 的防御机制吗?
stackoverflow 有个人说他完美解决了这个问题,是 xss clean 的原因。
:) God damn URLDECODE, I have looked at the code in URI.php but the xss clean is doing the job so I missed it. Thank you now everything is perfect. – RaduM
于是我找到了 core/security.php 下的 xss_clean() 函数。把函数体代码全部注释掉,发现输入还是会把 %04 过滤掉,显然也不是 xss 的问题。
4. 问题出在 _clean_input_data() 函数
重新回到 Input.php,发现 _clean_input_data 与 _clean_input_keys 有联系。
1 | $new_array [ $this ->_clean_input_keys( $key )] = $this ->_clean_input_data( $val ); |
于是把 _clean_input_data() 的函数体注释掉,竟然输入没被过滤了。继续缩小范围,发现是这段代码惹得祸:
3 | $str = remove_invisible_characters( $str ); |
5. 元凶找到了 remove_invisible_characters() 函数
那么 remove_invisible_characters() 这个函数是什么呢?
这个函数在 core/Common.php中,我把它揪出来:
01 | function remove_invisible_characters( $str , $url_encoded = TRUE) |
03 | $non_displayables = array (); |
10 | $non_displayables [] = '/%0[0-8bcef]/' ; |
11 | $non_displayables [] = '/%1[0-9a-f]/' ; |
14 | $non_displayables [] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S' ; |
18 | $str = preg_replace( $non_displayables , '' , $str , -1, $count ); |
看这么几行代码:
3 | $non_displayables [] = '/%0[0-8bcef]/' ; |
4 | $non_displayables [] = '/%1[0-9a-f]/' ; |
明确了吧,他会把%0与%1开头的3个字符过滤掉。直接把这个注释掉,问题解决。
记录这个问题解决的思维全过程。