xss与浏览器编解码

今天看道哥的白帽子讲web安全中XSS的防御部分,不太明白,所以就查了相关的服务器编码与浏览器解码,做一下小总结吧。

参考:https://security.yirendai.com/news/share/26 这篇文章真是一场解惑的及时雨

浏览器解析过程

浏览器在解析HTML文档时无论按照什么顺序,主要有三个过程:HTML解析、JS解析和URL解析,每个解析器负责HTML文档中各自对应部分的解析工作。下面以一篇HTML文档解析来简单的讨论下解析器如何协同工作的。

首先浏览器接收到一个HTML文档时,会触发HTML解析器对HTML文档进行词法解析,这一过程完成HTML解码并创建DOM树,接下来JavaScript解析器会介入对内联脚本进行解析,这一过程完成JS的解码工作,如果浏览器遇到需要URL的上下文环境,这时URL解析器也会介入完成URL的解码工作,URL解析器的解码顺序会根据URL所在位置不同,可能在JavaScript解析器之前或之后解析。

深入理解

下面我们结合具体示例来讨论下浏览器的解析原理过程和XSS复合编码的一些内容:

1
<a href="javascript:alert(1)">test</a>

针对上述a标签我们分析一下该环境中浏览器的解析顺序,首先HTML解析器开始工作,并对href中的字符做HTML解码,接下来URL解析器对href值进行解码,正常情况下URL值为一个正常的URL链接,URL解析器工作完成后是不需要其他解码的,但是该环境中URL资源类型为JavaScript,因此该环境中最后一步JavaScript解析器还会进行解码操作,最后解析的脚本被执行。

整个解析顺序为3个环节:HTML解码— URL解码— JS解码

我们对href值做一些编码的转换,对照刚才分析的解析过程,思考一下脚本是否会正常执行?

Test1: 对javascript:alert(1); URL编码

Test2: 对javascript html实体编码;对alert(2); url编码

Test3: 三层复合编码

  1. JS编码:<a href="javascript:\u0061\u006c\u0065\u0072\u0074(3)">test3</a>

  2. URL编码:

    <a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(3)">test3</a>

  3. HTML编码:

    <a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#51;&#49;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#54;&#51;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#51;&#53;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#55;&#37;&#51;&#50;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#55;&#37;&#51;&#52;&#40;&#51;&#41;">test3</a>

html在线测试:

1
2
3
4
<body>
<a href="6A%61%76%61%73%63%72%69%70%74:%61%6C%65%72%74%28%31%29">test1</a>
<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:%61%6C%65%72%74%28%32%29">test2 </a>
<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#51;&#49;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#54;&#51;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#51;&#53;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#55;&#37;&#51;&#50;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#55;&#37;&#51;&#52;&#40;&#51;&#41;">test3</a></body>

测试结果:test1无法弹窗;test2、test3成功弹框

思考:

Test1: HTML解码,URL解码为:javascript:alert(1); 然后js执行。emmm… 我是像上面这样想的,但是为什么执行的时候是404呢?这就涉及到URL解析中的一个细节,不能对协议进行编码,否则url解析器会认为它无类型,从而javascript没有被解码,自然就不会被执行了。于是404…

Test2: HTML解码javascript;URL解码alert(1); JS解析完成执行操作

Test3: HTML解码为编码第二步,继续HTML解码出javascript,URL解码,JS解码并执行

扩展:

场景分析:

1
<img src=x onclick="{$value}" >

如果$value用户可控,如输入为alert(‘m3lon’);那么如果防御XSS?

如果只进行html实体编码,那浏览器直接html解码—> JS解码执行 肯定不可以的

那如果加上一层JS编码呢?即JS编码—>html编码

浏览器解码顺序为:html解码—>JS解码,按理说也会执行,但是实际上不会,为什么呢?

​ 这里有一个关键点:在js中,单引号,双引号和圆括号等属于控制字符,编码后将识别为文本字符(而不是控制字符)。所以对于防御来说,应该编码这些控制字符。下面这种方式可以解析。
  <img src="1" onerror=\u0061\u006c\u0065\u0072\u0074('\u0031')>

总结

文章一方面浅谈了html解析过程,深入的解析可以看下面第二个链接,时间有限,就先不深入研究了;另一方面重点描述了URL解码与JS解码中的两个关键点:

  • url的协议名不可以被编码,否则url解码将视为无协议

  • JS自解码会将js中的控制字符如:单双引号、圆括号等识别为文本字符,只显示,不解析

参考:

坚持原创技术分享,您的支持将鼓励我继续创作!