当前位置: 首页 > 后端技术 > PHP

PHP 源码探秘 - 在解析外部变量时的一个 BUG

时间:2023-03-29 22:55:49 PHP

PHP源码探秘——解析外部变量时的一个BUG我的直播PHP进阶之路Bug修复有朋友向我描述了一个bug,让我帮忙看看到底是怎么回事。原来他有一个表格,如下。但是有个前端插件会动态插入两个input,最后ajax提交是Backend当我们使用php接收echofile_get_contents('php://input');echo"\n";var_export($_POST);echo"\n";echoPHP_VERSION;结果为id%5B%5D=1&id%5B%5D_text=a&id%5B%5D=2&id%5B%5D_text=barray('id'=>array(0=>'1',1=>'a',2=>'2',3=>'b',),)7.0.10使用nodejstryvarhttp=require('http');varquerystring=require('querystring');varpostHTML=''+'<输入type="text"name="id[]_text"value="a">'+''+'';http.createServer(function(req,res){varbody="";req.on('data',function(chunk){body+=chunk;console.log(body);body=querystring.parse(body);console.log(body);});req.on('end',function(){res.writeHead(200,{'Content-Type':'text/html;charset=utf8'});res.write(postHTML);res.end();});}).listen(3000);控制台输出是id%5B%5D=1&id%5B%5D_text=a&id%5B%5D=2&id%5B%5D_text=b{'id[]':['1','2'],'id[]_text':['a','b']}总结接收外部变量时,nodejs中会将多个相同的外部变量放在一个数组中,而php中后者会覆盖前者,如果需要传递一个数组变量,变量名后面加[],不兼容,ok,语言特性可以接受。但是在php中,在解析id[]_text的数据时,转成了id[],有点坑。rfc中没有这方面的规定,否则不会出现两种语言解析不一致的情况。源码分析就是PHP后台在分析问题。那只能从源码中找出来,看看PHP是如何解析post数据的。我把子进程数改成1,然后调试gdb-p22892...(gdb)b/data/soft/php-7.1.10/main/php_variables.c:php_register_variable_exBreakpoint1at0x812877:file/data根据到pid/soft/php-7.1.10/main/php_variables.c,line70.(gdb)ibNumTypeDispEnbAddressWhat1breakpointkeepy0x0000000000812877inphp_register_variable_exat/data/soft/php-7.1.10/main/php_变量。c:70(gdb)(gdb)cContinuing.Breakpoint1,php_register_variable_ex(var_name=0x7fb5b9056218"id[]",val=0x7ffff23dacd0,track_vars_array=0xf114a0)在/data/soft/php-7.1.10/main/php_variables.c:7070if(track_vars_array&&Z_TYPE_P(track_vars_array)==IS_ARRAY){(gdb)bt#0php_register_variable_ex(var_name=0x7fb5b9056218"id[]",val=0x7ffff23dacd0,track_soft_array=0xf114a0/114a0)在1vari/mains.php_c:70#10x00000000005af0d1inphp_sapi_filter(arg=,var=0x7fb5b9056218"id[]",val=0x7ffff23dad48,val_len=1,new_val_len=0x7ffff23dad40)at/data/soft/php-7.1.10/ext/filter/filter.c:465#20x00000000008135d0inadd_post_var(arr=0x7ffff23dce50,var=0x7ffff23dcda0,eof=)at/data/soft/php-7.1.10/main/php_variables.c:308#3add_post_vars中的0x0000000000813ce6(content_type_dup=,arg=0x7ffff23dce50)在/data/soft/php-7.1.10/main/php_variables。c:324#4php_std_post_handler(content_type_dup=,arg=0x7ffff23dce50)at/data/soft/php-7.1.10/main/php_variables.c:361#50x000000000080cfe0insapi_handle_post(arg=)at/data/soft/php-7.1.10/main/SAPI.c:174#60x00000000008133cfinphp_default_treat_data(arg=0,str=0x0,destArray=)at/data/soft/php-7.1.10/main/php_variables.c:423#70x000000000066c581在mbstr_treat_data(arg=0,str=0x0,destArray=0x0)在/data/soft/php-7.1.10/ext/mbstring/mb_gpc.c:69#80x0000000000812463在php_auto_globals_create_post(name=0x7fb5b1ddf768)at/data/soft/php-7.1.10/main/php_variables.c:720#90x000000000084125finzend_activate_auto_globals()at/data/soft/php-7.1.10/Zend/zend_compile.c:1681#100x000000000081282einphp_hash_environment()at/data/soft/php-7.1.10/main/php_variables.c:690#110x0000000000804c11inphp_request_startup()at/data/soft/php-7.1.10/main/main.c:1672#120x00000000000918282inmain(argc=,argv=)at/data/soft/php-7.1.10/sapi/fpm/fpm/fpm_main.c:1904(gdb)那么我们看php_register_variable_ex怎么写的,源代码精简了下,如下#include#include#include#includevoidphp_register_variable_ex(char*var_name);typedefunsignedcharzend_bool;intmain(){char*var_name="id1.2[]_3";php_register_variable_ex(var_name);返回0;}voidphp_register_variable_ex(char*var_name){char*p=NULL;char*ip=NULL;/*索引指针*/char*index;字符*var,*var_orig;size_tvar_len,index_len;zend_boolis_array=0;断言(var_name!=NULL);/*忽略变量名中的前导空格*/while(*var_name==''){var_name++;}/**准备变量名*/var_len=strlen(var_name);var=var_orig=malloc(var_len+1);memcpy(var_orig,var_name,var_len+1);/*确保我们在变量名中没有空格或点(不是二进制安全的)*/for(p=var;*p;p++){if(*p==''||*p=='.'){*p='_';}elseif(*p=='['){is_array=1;ip=p;*p=0;休息;}}var_len=p-变量;printf("var\t%s\n",var);printf("var_len\t%zu\n",var_len);}根据php_register_variable_ex中的规则:和.数组的key是[前面的字符串,后面的舍弃上面我模拟了当提交一个名字为id1.2[]_3的表单时,输出结果为varid_1_2var_len6想一想为什么上面的替换规则在官方手册http://php.net/manual/zh/lang...变量名中的点和空格被转换为下划线。但是我没有看到在命名中连接字符串后面没有使用[]的描述。是因为提取物吗?如果数组key中有[],则不能正常执行。$foo["id"]=1;$foo["id[]_text"]=2;var_export($foo);extract($foo);var_export(get_defined_vars());试过上面的代码,也证实了我的ideaid[]_text的值直接丢失了。因此,当php接受这样具名的(foo[]boo)外部变量名时,是不符合规范的,需要手动文档补全;当php接受这样一个不符合命名规范的外部变量(foo[]boo)时,是强制转换成数组,还是直接丢弃?后续处理方案我提交了一个bughttps://bugs.php.net/bug.php?...官方修复:完整说明文档http://php.net/manual/zh/lang...php8可能有个开关控制是否转换外部变量https://bugs.php.net/bug.php?……