浏览器解码机制
浏览器解码过程
HTML --> JS -->CSS --> URL
HTML解析
1、 字符实体以&开头+预先定义的实体名称+;分号结束,如“<”的实体名称为<
2、 字符实体还可以以&
开头+#
符号+字符在 ASCII 对应的十进制数字+;分号结束,如<的实体编号为<
一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它遇到了一个<
符号,只要后面没有跟/
就会进入标签开始状态(tag open state)
然后转变到标签名状态(tag name state)
,前属性名状态(before attribute name state)
…最后进入数据状态(data state)
并释放当前标签的token。当解析器处于数据状态(Data state)
时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。
HTML 五类元素:
- 空元素(empty element)
在 HTML 中,通常在一个空元素上使用一个闭标签是无效的。例如<input type="text"></input>
的闭标签是无效的 HTML。
在HTML中有以下空元素
<area>
<base>
<br>
<col>
<colgroup> when the span is present
<command>
<embed>
<hr>
<img>
<input>
<keygen>
<link>
<meta>
<param>
<source>
<track>
<wbr>
空元素不能容纳任何内容(因为它们没有闭合标签,没有内容能够放在开始标签和闭合标签中间)
-
原始文本元素(Raw text elements) 可以容纳文本
<script>、<style>
存在一条特性: Raw text elements 类型标签下的所有字符实体编码都不会被 HTML 解码。HTML 解析器 解析到 script、style 标签的内容块(数据)部分时,状态会进入 Script Data State,该状态并 不在我们前面说的会解码字符实体的三条状态之中。
因此,<script>alert(9);</script>
这样字符实体并不会被解码,也就不会执行 JS。 -
RCDATA元素(RCDATA elements) 可以容纳字符和文本引用
textarea、title
解析器解析到 textarea、title标签的数据部分时,状态会进入 RCDATA State。
处于 RCDATA State 状态时,字符实体是会被解析器解码的。但是里面的 JS 同样还是不会被执行,原因还是因为解码字符实体状态机不会进入标签打开状态(TagOpen State),因此里面的<script>
并不会被解析为 HTML 标签。
例如:
<textarea><script>alert(5)</script></textarea>
- 外部元素(Foreign elements) 例如MathML命名空间或者
svg
命名空间的元素 可以容纳文本、字符引用、CDATA段、其他元素和注释 - 基本元素(Normal elements) 除了以上4种元素以外的元素 可以容纳文本、字符引用、其他元素和注释
JavaScript解析
HTML事件属性
JS编码的规则相对来说比较严谨,它对除了阿拉伯数字和字母外的东西都进行了一个编码
最常用的如\uXXXX
这种写法为Unicode转义序列,表示一个字符,其中xxxx表示一个16进制数字,如<
Unicode编码为\u003c
。
如果有代码
<img src="1" onerror=\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029>
无法执行
我们以浏览器的视角来看:首先读到<
开始读取标签,然后读到onerror
调用js解析器。在js中,单引号,双引号和圆括号等属于控制字符,编码后将无法识别。所以对于防御来说,应该编码这些控制字符。下面这种方式可以解析。
<img src="1" onerror=\u0061\u006c\u0065\u0072\u0074('\u0031')>
若 Unicode 转义序列存在于控制字符中,那么它会被解码但不会被解释为控制字符,而会被解释为标识符或字符串字符的一部分。 控制字符即'、"、()
等
例如
<script>alert\u0028"xss"); </script>
其中将(
Unicode编码成了\u0028 解码后它将不再是控制字符而作为了alert( 的一部分
总结: Unicode编码不能编码控制字符
实例1:
//alert 和 括号里的 1 2 被编码
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
这JS不会被执行,因为int类型1、2被Unicode编码 反编码后会被按照成字符串类型处理。解决方法:使用ASCII数字或者加" "
URL解析
通用 URI 的格式如下:
[协议名]://[用户名]:[密码]@[主机名]:[端口]/[路径]?[查询参数]#[片段 ID]
URL中scheme部分也就是协议部分必须是ASCII字符,即不能被任何编码,否则URL解析器会进入 No Scheme 状态
如:
<ahref="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29"></a>
其中 JavaScript是scheme部分,它被编码了,所以会进入 No scheme 状态
其中的 : 也不能被编码。
解析顺序
**HTML解析总是第一个**,URL解析和JS解析都要根据位置不能有所不同
实例1:
<a href=# onclick="window.open('UserInput')"></a>
当解析到<
标签后进行HTML解析,当解析到onclick
时进行JS解析,当解析到window
时进行URL解析
实例2;
<a href="javascript:window.open('UserInput')">
解析顺序 HTML URL JS 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(15)"></a>
逆向来看我们首先进行HTML解码 得到
<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(15)"></a>
然后进行URL解码
<a href="javascript:\u0061\u006c\u0065\u0072\u0074(15)"></a>
然后进行JS解码得到
<a href="javascript:alert(15)"></a>
https://www.cnblogs.com/b1gstar/p/5996549.html
https://www.freebuf.com/articles/web/222849.html
https://segmentfault.com/a/1190000039396389