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

Java和Php获取客户端cookie的方式不同导致的bug

时间:2023-03-30 02:23:26 PHP

遇到了Java和Php获取客户端cookie的方式不同导致的跨系统问题。所以写了这篇博客,整理一下相关的知识。实验下面通过两个简单的实验,看看Java和Php在web请求中获取cookie的区别。下面我把http请求的相关信息和服务器输出的结果贴出来。Java请求信息GET/HTTP/1.1Host:localhost:7003...Cookie:test2=ab+cd;test1=ab%2Bcdserver@Controller@Slf4jpublicclassMainController{@AutowiredprivateHttpServletRequest请求;@GetMapping("/")public@ResponseBodyStringindex(){Cookie[]cookies=request.getCookies();if(null!=cookies){for(Cookiecookie:cookies){log.info(cookie.getName()+"="+cookie.getValue());}}返回“索引”;}}控制台输出2019-05-1618:03:32.770INFO10114---[nio-7003-exec-1]net.mengkang.demo.MainController:test2=ab+cd2019-05-1618:03:32.770信息10114---[nio-7003-exec-1]net.mengkang.demo.MainController:test1=ab%2BcdPhpGET/HTTP/1.1Host:localhost:8084...Cookie:test2=ab+cd;test1=ab%2Bcd服务器var_exprot($_COOKIE);array('test2'=>'abcd','test1'=>'ab+cd',)result通过对比发现,Java没有对cookie数据做任何处理,但是PHP会通过default,导致两个系统获取同一个cookie时,结果不一致的bug。类似的问题是PHP解析外部变量时的一个BUGP。php源码分析主要看两段源码main/php_variables.cext/standard/url.cSAPI_APISAPI_TREAT_DATA_FUNC(php_default_treat_data){...switch(arg){casePARSE_GET:casePARSE_STRING:separator=PG(arg_separator).input;休息;casePARSE_COOKIE:分隔符=";\0";//在我们的浏览器中可以看到,请求头中的cookie分隔符就是这个break;}var=php_strtok_r(res,separator,&strtok_buf);while(var){val=strchr(var,'=');if(arg==PARSE_COOKIE){/*从cookie名称中删除前导空格,多cookie标头需要where;后面可以跟一个空格*/while(isspace(*var)){var++;}if(var==val||*var=='\0'){gotonext_cookie;}}if(++count>PG(max_input_vars)){php_error_docref(NULL,E_WARNING,"输入变量超出"ZEND_LONG_FMT"。对我来说在php.ini中增加max_input_vars的限制变化。",PG(max_input_vars));break;}if(val){/*有一个值*/size_tval_len;size_tnew_val_len;*val++='\0';php_url_decode(var,strlen(var));val_len=php_url_decode(val,strlen(val));val=estrndup(val,val_len);if(sapi_module.input_filter(arg,var,&val,val_len,&new_val_len)){php_register_variable_safe(var,val,new_val_len,&array);}efree(val);}else{size_tval_len;size_tnew_val_len;php_url_decode(var,strlen(var));val_len=0;val=estrndup("",val_len);if(sapi_module.input_filter(arg,var,&val,val_len,&new_val_len)){php_register_variable_safe(var,val,new_val_len,&array);}efree(val);}next_cookie:var=php_strtok_r(NULL,separator,&strtok_buf);}if(free_buffer){efree(res);}}我们看到cookie的值会被php_url_decode操作执行,下面附上它的源码,并添加一段测试代码#include#include#includestaticintphp_htoi(char*s){int值;诠释c;c=((unsignedchar*)s)[0];如果(isupper(c))c=tolower(c);值=(c>='0'&&c<='9'?c-'0':c-'a'+10)*16;c=((unsignedchar*)s)[1];如果(isupper(c))c=tolower(c);值+=c>='0'&&c<='9'?c-'0':c-'a'+10;return(value);}size_tphp_url_decode(char*str,size_tlen){char*dest=str;字符*数据=海峡;while(len--){if(*data=='+'){*dest='';}elseif(*data=='%'&&len>=2&&isxdigit((int)*(data+1))&&isxdigit((int)*(data+2))){*dest=(char)php_htoi(数据+1);数据+=2;长度-=2;}else{*dest=*data;数据++;目的地++;}*dest='\0';returndest-str;}intmain(){chara[6]={"ab+cd"};php_url_decode(a,strlen(a));printf("%s\n",a);return0;}上面的php_url_decode使用了php_htoi,这是因为urlencode是基于rfc1738中的,除-_外的所有非字母数字字符。字符串中的将替换为百分号(%)后跟两位十六进制数htoi,即ConvertingHexadecimalDigitsIntoIntegers。然后将计算出的整数转换为char,处理完后存回字符数组。总结php端的$_COOKIE数据是经过urldecode的二手数据,导致与JAVA端的cookie值不同。编码扩展讨论rawurlencode和urlencode有什么区别?手册中的解释是:urlencode返回一个字符串,这个字符串中除-_外的所有非字母数字字符。将替换为百分号(%)后跟两个十六进制数字,空格编码为加号(+)。此编码与WWW表单POST数据相同,并且与application/x-www-form-urlencoded媒体类型相同。由于历史原因,此编码与?RFC3986编码(请参阅rawurlencode())在编码空间中作为加号(+)不同。PHPAPIsize_tphp_raw_url_decode(char*str,size_tlen){char*dest=str;字符*数据=海峡;while(len--){if(*data=='%'&&len>=2&&isxdigit((int)*(data+1))&&isxdigit((int)*(data+2))){#ifndefCHARSET_EBCDIC*dest=(char)php_htoi(data+1);#else*dest=os_toebcdic[(char)php_htoi(data+1)];#endifdata+=2;长度-=2;}else{*dest=*data;数据++;目的地++;}*dest='\0';returndest-str;}通过源码可以看出+处理没有了。请求编码讨论GET当我们在url中传递+时,浏览器默认不会帮我们进行urlencode操作,但是当php服务器取值的时候(或者上面的代码),会执行urldecode,导致+中的要删除的网址。这也很容易被发现。变量转储($_GET);curlhttp://localhost:8084/a.php\?a=\bbb+carray(1){["a"]=>string(5)"bbbc"}POST当我们制作表单提交apost请求,默认的表单编码规范是application/x-www-form-urlencoded,这样浏览器会自动对我们的数据进行一次urlencode编码,然后php服务器收到$_POST数据再进行urldecode。当我在表单中提交时一段ab+cd内容,请求数据如下POST/a.phpHTTP/1.1...Host:localhost:8084Content-Type:application/x-www-form-urlencoded...Cookie:test2=ab+裁谈会;test1=ab%2BcdpostData=ab%2Bcdserver#a.phpvar_dump($_POST);var_dump(file_get_contents("php://input"));outputresultarray(1){["postData"]=>string(5)"ab+cd"}string(16)"postData=ab%2Bcd"另外一种情况,如果我们post表单的执行代码是multipart/form-data,浏览器不会对数据进行编码,服务器端也不会对数据进行解码。所以我们在配置url参数和cookies的时候,一定要注意url编码的问题。