噩梦始于XSS注入修订
在之前的工作中,公司出于安全合规考虑,需要全局盘点页面进行XSS注入的修订。
这是一套有点年纪并且混合着vue和jQuery的代码,便采用了最常见的转义方式来进行修订。
encodeHtml : function(str) {
if (!Util. isString(str)) {
return str;
}
var s = "";
if (str. length == 0) return "";
s = str. replace(/&/g, "&");
s = s. replace(/</g, "<");
s = s. replace(/>/g, ">");
s = s. replace(/ /g, " "); //这里就是万恶的开始
s = s. replace(/\'/g, "'");
s = s. replace(/\"/g, """);
s = s. replace(/\n/g, "<br>");
return s;
},
uncodeHtml : function(str) {
if (!Util. isString(str)) {
return str;
}
var s = "";
if (str. length == 0) return "";
s = str. replace(/&/g, "&");
s = s. replace(/</g, "<");
s = s. replace(/>/g, ">");
s = s. replace(/ /g, " ");
s = s. replace(/'/g, "\'");
s = s. replace(/"/g, "\"");
return s;
},
但是随着XSS修订完成后没多久,莫名其妙的bug却突然接踵而至:当配置的名称包含空格时,采用这个名称的部分信息界面会出现报错,无法获取该名称的相关信息。
深入探究
明明使用的是一样的接口啊,怎会出现一个报错,另一个不报错的现象?
通过对比两个接口传递的参数,点击查看网址编码格式的数据:
可以很明显地发现,正常的接口,其载荷的名称 是name:33+3
,而 +
就是空格的意思,所以其传递的名称就是正常的 33 3
。
反观错误的接口,其载荷的名称是奇怪的 name:33%C2%A03
,奇怪,%C2%A0
是什么?为什么空格会变成这串东西?
通过 UTF-8编码表 比对,我们可以得知 %C2%A0
即 C2=194 A0=160
,这个在编码表中对应的是:
啊哈!NO-BREAK SPACE
,正是 SPACE
那前面一串的修饰,注定使他成为了特殊的存在,和那些平平无奇的普通空格不是一类!
特殊的空格-
根据描述,这是一种十分特殊的空格。
众所周知,在页面的元素中,一旦你输入了多个空格,那么页面会自动压缩显示成只有一个空格的样子。
如果有人玩的比较花,就是不想缩在一起,那么
就派上用场了~
任何时间,任何地点,
,(认真办案) 绝不压缩!
所以,之所以接口错误,拿不到该名称的相关信息,就是因为他所拿到的名称里,普通的空格早已变成了特殊的
空格 !
恶心的代码,无奈的码农
那为什么,普普通通的、包含空格的名称,经页面一转手,就变成了特殊的空格呢?
来仔细看看代码探究一下吧:
细细品味前人的代码,让人五味杂陈。
明明都用vue重构了,为什么还要用jQuery来绑定点击事件?
明明都用vue重构了,为什么还要用$(this).text()
来获取该名称?
正是着一串莫名其妙让人懵逼的代码,在经过之前的XSS注入修订转义,原本普普通通老老实实的空格一秒提升逼格,成为了不老实,不压缩的特殊空格——
!
着手修订吧!
至于修订方法也十分简单,不要传递这种特殊的空格即可,但是我还是无法忍受这种写法,最终使用@click
来直接传递该行数据的信息解决。
但是,根据旧有的代码分析,我发现了新的BUG,通过url来传递信息(好老的代码啊!),在解析数据时通过 &
符号来分割,如果我配置了包含 &
符号的名称,那不是也得挂掉?(永远都修不完的BUG啊!)🤮🤮
太长了不想看?来看看总结
根据上述一连串的分析,最终可以确定,之前的XSS修订的转义,使得从页面DOM元素直接获取的名称,其名中的普通的空格(UTF8对应32)变成了特殊的空格
(UTF8对应194 160,即C2 A0),而这特殊的空格通过接口传递给设备,导致设备不识别该空格而找不到相关的信息,导致接口错误。