隐形的字符

发现问题

我解决过无数的编码问题,认为凭着这些年的经验只要是关于编码的问题都会被一眼识破。万万不会想到有一天被几个隐形字符折磨;

很平常的一天,有用户发邮件说自己提交一个md5串时一直提示『字符格式超过32位』,我想肯定是用户输入的md5串有问题。然后使用用户提交的md5串在测试环境和生产环境测试都是好的。用户那边一直在等解决办法,我首先查了用户的请求日志,一一对比之后发现参数没有问题,然后我模仿用户请求在页面上填了和用户一样的参数,请求都是成功的。

这就奇怪了,用户提交之后我对md5串做过trim(),肯定不是首尾有空格的问题。我把我的请求日志和用户的请求日志放一起做对比,如下:

用户:​/api/v1?type=2&key=asdfghjkqwer ​

开发:/api/v1?type=2&key=asdfghjkqwer

如上两个字符串真看不出有什么问题,然后我直接通过CURL发起请求,结果还是成功。这个问题百思不得其解,不过我心里已经确定这一定是一个特别傻的问题,只是没排查到点上。

问题追踪

既然是很傻的问题,再走一遍流程肯定能找到问题,请求流程如下:

  1. 用户提交请求到服务器;
  2. 服务器收到请求URL并记录日志;
  3. 验证用户md5串,验证失败;
  4. 记录验证失败的日志,并返回给用户提示信息;

如果是正常的数据请求结果却报错了,那肯定是第二步有问题。但是第二步又不会做更新操作,理论上不应该出错,所以最终还是落在了日志上。这次我将日志全量复制(这里有个细节,我在复制参数的时候喜欢双击两下鼠标选中再复制,所以之前的CURL请求并不是全量复制),包括空格都复制到了请求中,一测试果然复现了用户提交的错误信息。最后还是在Chrome的调试窗口中看到多出一个红点,才看到『隐形的字符』的真身,导致错误的原因就是『隐形的字符』。

zero-width space (ZWSP) 是一种在屏幕上的不可见字符,UNICODE编码为『u0020u200b』。

用户:​/api/v1?type=2&key=asdfghjkqwer ​

开发:/api/v1?type=2&key=asdfghjkqwer

用户发的请求串末尾其实跟了一个不可见字符,显示效果和空格一样,但却不是空格,所以trim()方法无效。

解决方案

为什么用户会带上这个『zero-width space』不可见字符我也无从得知,最后我对trim()函数做了扩展,将这种不可见字符一起过滤。虽然这是一个小问题,但是值得我们去深入的思考。比如我们一句简单的代码就能让用户用的很舒服,即使用户输入不规范,带了某些不可见的字符。再比如我们应不应该允许用户使用这种不可见字符?比如写在昵称,介绍,评论里,虽然不可见字符不会对数据造成危害,但是对前端显示会有影响,比如写一条 zero-width space 字符的微博,复制 “ ​ ​ ​​​​​” 引号中的内容发一篇微博看看效果。