某个我常去的论坛上,时不时会出现贴子布局混乱的问题,如下图(因为原贴太长,所以PS过以减小图片面积):
很明显的,左上角出现的“–>”很好的暗示了我们,HTML中有点乱套了。而且这也常常是网站系统可能存在XSS漏洞的最初现象之一,我不邪恶,不过担心别人会邪恶,所以打算把问题原因找出来,告诉网站管理员。
长期的实践中,我曾对这个问题总结过一个规律:这个问题只在Firefox中出现,并且会乱掉的贴子多半是因为某一个特定ID的人参与了讨论,开始混乱的位置常常是在那个ID的人的签名档往上一点的位置。
今天为了分析解决这个问题,你会怎么推理?我的推理是:由于签名档允许部分HTML,一些不规范的HTML标签导致了页面混乱。
于是去查看HTML源文件,原始文件太长,所以这里就示意一下:
<div>
<!-- Blah blah blah <br />
-------------- <br />
Foo bar Foo bar
-->
Blah blah blah <br />
-------------- <br />
Foo bar Foo bar
</div>
<div style="maxHeightIE: 160px;">
My signautre. My signautre. My signautre. My signautre. My signautre.
</div>
是的,推理错了。显而易见的,签名档中并没有问题,而且HTML代码似乎也没有什么异常的地方。把这段HTML放到一个新的空白网页中,显示也一切正常,没有发生混乱。
也许是网页中有一些不可见的字符,而且复制粘贴后就没有了?Emacs帮我证明了不可见字符这一点,这个网页在我的Linux平台上是以LF字 符做为换行的,但那个贴子内容中的换行符却是Windows中的CR LF。于是我把CR字符都删掉,问题依旧。难道还有更神秘的不可见字符在搞鬼?用二进制文件编辑器查看这个文件,一切正常。
再分析一下,在网页上出现混乱的起始点是–>符号,那看来是在那段注释中就已经出现问题了。用Firefox自带的查看源代码的功能,可以看到,它只把注释的前两行识别成为注释,标记成绿色,从Foo bar那行开始,就变成正常的HTML标签的颜色了。
为什么Firefox会错误的识别HTML注释标签的结尾?难道因为这个文件编码有问题(原始文件中是有中文的,例子中只写了英文字符)?(大家还记得Windows记事本对待联通和移动的不同态度吧) 把网页由GBK编码重存为UTF-8编码,问题依然。
没招了,借助神奇的Google,换了好几次关键词之后,终于用“Firefox comment bug”这个关键词找到了有用的信息。
简单的说,HTML4规 范中,注释的起始标签是<!,注释的起始和结束符号是–,注释的结束标签是>。当然,这是一个很严格的形式化的定义,但这跟我们通常想象中 的注释由<!–开始,以–>结束是不一样的。因为HTML4规定,结束符号–和结束标签>间可以有空格隔开,也就是说,并不要求 –和>连在一起写才表示注释结束。规范建议在注释中不要出现两个或以上“-”出现的情况。
规范中没有说结束符号–和结束标签>之间除了空格以外可不可以有别的字符出现,Firefox很激进把它定义为允许出现,所以就带来了我们所看到的问题。
事情还没完,那为什么把这段代码单独贴成一个HTML文件测试就没有问题呢?因为没有指定DOCTYPE时,Firefox不会按HTML4的规范去解析。如果你在测试文件开头加上HTML4的DOCTYPE就可以重现这个问题了:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
还没完,那为什么这个问问往往出现在某个特定ID参与过的贴子上呢?因为那个人不喜欢用论坛的“引用”功能,他喜欢复制前一个人说过的话,用———隔开,下面写回复。或者就是用———来分隔他的贴子正文的几段内容。
为什么那个BBS系统中要把贴子的内容输出两遍,其中一遍是在注释中呢?不知道。有一种可能性是开发者还不熟悉网页后台系统的开发,他总是习惯用HTML中的注释语法去注释后台他不想要的代码。这种做法基本上是错误的,但犯这样错误的人绝不在少数。
继续,虽然Firefox这样做没有违背规范,但多少有点违背常识,为什么它不修复这个问题呢?这是因为它要兼顾SGML的解析——在那里“–”有着很重要功能。不过由于HTML5中对注释的语法做了更严密的规定,Mozilla说,他们准备在后续的版本中修复这个问题。
最后,Firefox这个问题会不会带来原本可能不存在的XSS攻击?应该说可能性是有的,但主要问题还是会出在网站开发者的身上。比如你在后台调试时,输出了未转意的用户输入内容,并用HTML的注释注释掉了,你以为是安全的,但由于这个问题的存在,带来了XSS攻击的可能性。
这个故事还告诉我们一些道理,不写了。因为我已经把分析/走弯路的过程写得很详细了,那些道理都是简单易见好理解的,只是常常我们做得不太好。