Zero-width space(ZWSP)的Unicode编码为U+FEFF,二进制编码为“\xE2\x80\x8E”,对该字符编码 encodeURI(url)后为 %E2%80%8E。根据维基百科描述,其主要用于后台处理字符边界而又无需可见空格的情况。简言之就是该字符具有空格的功能,但宽度为零。这样的字符还有%E2%80%A8,%E2%80%A9,%E2%80%8B,%E2%80%8C,%E2%80%8D,%E2%80%8E等。这些字符一般打不出来,基本上都是从其他地方(如word,markdown)中复制过来的,且肉眼不可见。如果该字符出现在url中时,会使你的URL出现错误。
1、现象及原因
1.1 现象
一般封装的请求器报出类似于因为地址解析错误不能执行请求的错误,而打印出来的请求地址肉眼看来是正确的。这时候就应该考虑是不是出现了“没有宽度的空格”这种情况,即Zero-width space(ZWSP)。网址如下:
https://www.***.com/archives/js4?param=1
可以看到该网址是比较正常的,我们把该网址复制粘贴到txt中看一下:
可以看到该网址后面有个标记,这个就是不可见字符 %E2%80%A8,该字符会导致网络请求出现问题。
我们还可以对 url 进行 encodeURI(url) 编码,如果能够找到 %E2%80%8E 的编码,就说明 url 是含有 ZERO-WIDTH SPACE 的。
如果是在编辑器中有Zero-width space(ZWSP)字符,如上述的url,把光标定位到文本的最后,按删除键,可以发现我们要点击两次删除键才可以删除掉后面的空格。
- ZWSP的编码包括:%E2%80%A8,%E2%80%A9,%E2%80%8B,%E2%80%8C,%E2%80%8D,%E2%80%8E
1.2 出现的原因
出现该字符的原因一般是从其他地方复制过来的一段文字(如Word,MarkDown),粘贴上之后就可能会有多一个ZERO-WIDTH SPACE的情况,它是肉眼看不见的。
1.3 影响
- Zero-width space(ZWSP)字符如果出现在url中,会导致地址解析错误不能执行请求的错误。
- Zero-width space(ZWSP)字符如果出现提交的请求参数中,且该请求参数会加密传输,有可能会出现前后台加密结果不一致的问题。
2、代码复现
新建一个html文件,如test.html,内容如下:
<input type="text" id="have10" />
<input type="text" id="have20" />
<script>
var uniCodeStr = "\u0008";
var str = eval("'"+uniCodeStr + "'")
var have10 = "10";
var have20 = "20";
for(var i = 0;i < 10 ; i++ ){
have10 += str;
}
for(var i = 0;i < 20 ; i++ ){
have20 += str;
}
document.getElementById('have10').value = have10;
document.getElementById('have20').value = have20;
</script>
使用ie浏览器打开时显示如下:
谷歌浏览器打开时显示如下:
大家可以试一试,需要按10次退格键,才可以删到10的位置;需要按20次退格键,才可以删到20的位置。
是不是有一点恐怖?
看不到,但是又实实在在的存在。
3、场景
有这么几个场景,大家可以想象一下:
- 有人发了一个text文档,里面只有两个字母,但是接收时却卡死了电脑;
- qq上陌生人发来了两个字,手机却重启了;
- 数据库查询的时候,字段、sql看起来都对,但是却查不出想要的结果;
- url写的没问题,但是总是404~
4、解决方案
如果 url或提交的请求参数中出现Zero-width space(ZWSP)字符,解决方案是删除掉该字符就可以解决问题。代码中处理最简单的办法就是将内容编码后替换掉( repalce())不可见字符。
4.1 url不可见字符
url不可见字符处理脚本如下:
// 去除word不可见字符
var wordReg = /%E2%80%(A8|A9|8B|8C|8D|8E)/gi;
if (wordReg.test(encodeURI(url))) {
url = decodeURI(encodeURI(_content).replace(wordReg,''));
}
4.2 请求参数不可见字符
请求参数内容中包括不可见字符的处理脚本如下:
// 去除word不可见字符
var wordReg = /%E2%80%(A8|A9|8B|8C|8D|8E)/gi;
if (wordReg.test(encodeURIComponent(_content))) {
_content = decodeURIComponent(encodeURIComponent(_content).replace(wordReg,''));
}
- encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ’ ( ) 。其他字符(比如 :;/?😡&=+$,# 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。
4.3 网上的方案
- 替换方案一
str.replace("\xe2\x80\x8b", '');
- 替换方案二
str.replace(/[\u200B-\u200D\uFEFF]/g, '');
- 替换方案三
str.replace(/\u8203/g, '');
str.replace(/\uB200/g'');
- 替换方案四
str.replace(/(^[\s\u200b]*|[\s\u200b]*$)/g, '')
- 也可以先获取正常字符,然后join
var res = str.match(/\w/g);
str = res.join('');
评论区