XSS(Cross Site Scritp)跨站脚本攻击,它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。个人感觉跟Sql注入差不多,原因都是利用网站对于用户的信任,认为用户的输入都是合法规范的,从而没有对用户的输入进行良好的有效的过滤,html界面能够被攻击者插入js脚本,从而使某些用户利用这些漏洞完成网站攻击操作。
html解析器是一个状态机,其基本状态有如下四种:标签开始状态(Tag open state)、标签名状态(Tag name state)、结束标签打开状态”(End tag open state)、数据状态(Data State).注意每次发现一个完整的tag之后就会释放该token,其状态机如图:
这里举一个简单的栗子:
<html>
<body>
Hello world
</body>
</html>
其解析过程如下:
(1)初始状态为'Data state',当遇到字符’<’, 当前状态切换到”标签打开状态”。
(2)遇到字符’h’,创建一个新的起始标签标记,设置标记的标签名为空,当前状态切换至”标签名称状态”(Tag name state)。
XSS
XSS(Cross Site Scritp)跨站脚本攻击,它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。个人感觉跟Sql注入差不多,原因都是利用网站对于用户的信任,认为用户的输入都是合法规范的,从而没有对用户的输入进行良好的有效的过滤,html界面能够被攻击者插入js脚本,从而使某些用户利用这些漏洞完成网站攻击操作。
XSS可以分为三种形式,一种是反射型的,也叫非持久性XSS。这种类型的XSS是由于服务器端未对客户端传入的数据进行存储和任何过滤,直接返回客户端被浏览器渲染导致的。一般的利用方式是攻击者找到网站XSS漏洞之后,通过发邮件等方法使用户访问一段可以触发XSS攻击脚本的链接,一旦用户访问该链接触发恶意脚本,那么攻击成功。更为常见的情景就是在url中利用编码绕过常见的参数过滤逻辑从而达到注入恶意脚本的目的。第二种形式是持久型的XSS。攻击者使恶意脚本绕过前后端检测存储到服务器端中,一旦有用户访问改页面,恶意脚本没有经过转义被浏览器直接渲染就会触发攻击。这种XSS攻击范围广。第三种是Dom-based 的XSS,这种就是单纯的客户端攻击,利用
document.write
、innerHTML
等输出可以直接触发恶意代码。这三种方式导致XSS的发生,其原因就是没有对输入输出做有效的转义和过滤。所以要减少XSS的发生,应该做到客户端对服务器返回的数据完全不信任,服务器对客户端传递的数据也完全不能信任,对所有输入输出都做好转义过滤。
浏览器解析和字符编码
无论什么XSS攻击其代码都运行在浏览器环境,所以为了更好的防范XSS攻击,浏览器解析的过程只是是基本。我们都知道浏览器完成界面渲染的过程是解析DOM,生成CSSOM,然后生成渲染树,最后浏览器在解析HTML文档时无论按照什么顺序,主要有三个过程:HTML解析、JS解析和URL解析,每个解析器负责HTML文档中各自对应部分的解析工作。
HTML解析
解析可以分为两个子过程——语法分析及词法分析。
词法分析就是将输入分解为符号,符号是语言的词汇表——基本有效单元的集合。对于人类语言来说,它相当于我们字典中出现的所有单词。
语法分析指对语言应用语法规则。
解析器一般将工作分配给两个组件——词法分析器(有时也叫分词器)负责将输入分解为合法的符号,解析器则根据语言的语法规则分析文档结构,从而构建解析树,词法分析器知道怎么跳过空白和换行之类的无关字符
一些基本的概念:
字符实体是一个转义序列,它定义了一般无法在文本内容中输入的单个字符或符号。一个字符实体以一个&符号开始,后面跟着一个预定义的实体的名称(实体名称),或是一个#符号以及字符的十进制数字(实体编号)。注意,某些实体并不存在实体名称,但都存在实体编号。
知道了什么是字符实体,那什么是html字符实体(8进制或者16进制)就简单了。html中预留了一些字符,比如
<
和>
,用于标签的分隔。我们想在页面中展示这两个字符怎么办呢,肯定不能直接在文本中插入这两个字符,不然可能导致dom结构出错。但我们可以通过使用<;
和>
这种方式来进行该字符的展示。这里这两个就叫做<
和>
的html实体。PS:
<
实体编码为<
或者<
html中有五大元素分别是:
五类元素的区别如下:
好了了解完上面两个概念之后我们来了解一下html词法解析过程吧。
词法解析看这里
html解析器是一个状态机,其基本状态有如下四种:标签开始状态(Tag open state)、标签名状态(Tag name state)、结束标签打开状态”(End tag open state)、数据状态(Data State).注意每次发现一个完整的tag之后就会释放该token,其状态机如图:
这里举一个简单的栗子:
其解析过程如下:
(1)初始状态为'Data state',当遇到字符’<’, 当前状态切换到”标签打开状态”。
(2)遇到字符’h’,创建一个新的起始标签标记,设置标记的标签名为空,当前状态切换至”标签名称状态”(Tag name state)。
(3)重新从字符’h’开始解析,将解析的字符一个一个添加到创建的起始标签标记的标签名中,直到遇到字符’>’。此时当前状态切换至”数据状态”并释放当前标记,当前标记的标签名为’html’。
(4)解析后续的’
’和’(5)遇到字符串’解析html文档’,针对每一个字符,创建并释放一个对应的字符标记,解析完毕后,当前状态仍然处于”数据状态”。
(6)遇到字符’<’, 进入”标签打开状态”。
(7)遇到字符’/‘, 进入”结束标签打开状态”(End tag open state)。
(8)遇到字符’t’,创建一个新的结束标签标记,设置标记的标签名为空,当前状态切换至”标签名称状态”(Tag name state)。
(9)重新从字符’t’开始解析,将解析的字符一个一个添加到创建的结束标签标记的标签名中,直到遇到字符’>’。此时当前状态切换至”数据状态”并释放当前标记,当前标记的标签名为’title’。
(10)解析’’的方式与’
(11)对于后续的html标签和文本的解析,可以参照(1)~(16)的流程来解析。
(12)所有的html标签和文本解析完成后,状态切换至”数据状态”,一旦遇到文件结束标志符(EOF),则释放EOF标记。
HTML实体在如下三种情况下会被解码,在这些状态中HTML字符实体将会从“&#...”形式解码,对应的解码字符会被放入数据缓冲区中:
<div><span></span></div>
。但是解析器在解析这个字符引用后不会转换到“tag open state”。正因为如此,就不会建立新标签.只会在div中将<span>
输出`来<title><</title>
会输出<
.注意在浏览器解析RCDATA元素的过程中,解析器会进入“RCDATA状态”。在这个状态中,如果遇到“<”字符,它会转换到“RCDATA小于号状态”。如果“<”字符后没有紧跟着“/”和对应的标签名,解析器会转换回“RCDATA状态”。这意味着在RCDATA元素标签的内容中(例如”或者“”。因此在这中间并不会创建html元素,也不会执行脚本.比如<title><script>alert(1)</script></title>
这段html中script代码并不会生效<a href="javascript:alert(0)"></a>
会被解码为<a href="javascript:alert(0)"></a>
URL解析
url解析看这里,一般编码格式是
%
加上ascii码的两位数URL解析器也是一个状态机模型,从输入流中进来的字符可以引导URL解析器转换到不同的状态。
URL资源类型必须是ASCII字母
(U+0041-U+005A || U+0061-U+007A)
,不然就会进入“无类型”状态。例如,你不能对协议类型进行任何的编码操作,不然URL解析器会认为它无类型URL编码过程使用UTF-8编码类型来编码每一个字符。如果你尝试着将URL链接做了其他编码类型的编码,URL解析器就可能不会正确识别。(不能对要进行URL解析的字符串进行其他编码)
Javascript解析
javascript编码的形式有:unicode编码(\uXXXX一样的字符),8进制,16进制,这里主要考虑unicode编码
特别注意:
script
标签里的文本的都是原始文本,不存在html实体的不会被解析和解码的过程。这意味者你将脚本进行html编码之后插入script标签也不会成功,只会原样返回.因为这里已经交由javascript解析器进行解析了。我们可以将转义序列放在3个部分:字符串中,标识符名称中和控制字符中
ECMAScript程序中,在字符串常量中出现的Unicode转义序列会被当作字符串常量中的一个Unicode字符,并且不会被解释成有可能结束字符串常量的换行符或者引号
所以
console.log('\u0027)\\')
(\u0027是'的unicode码)只会输出'字符,而不会和(后面的'组合从而结束参数标识符中
Unicode转义序列(如\u000A\u000B)同样被允许用在标识符名称中,被当作名称中的一个字符。而将'\'符号前置在Unicode转义序列串(如\u000A000B000C)并不能作为标识符名称中的字符。将Unicode转义序列串放在标识符名称中是非法的
所以我们这样是可行的
<input type="button" name="demo" onclick="\u0061\u006c\u0065\u0072\u0074(1)">
(alert的转码)当用Unicode转义序列来表示一个控制字符时,例如单引号、双引号、圆括号等等,它们将不会被解释成控制字符,而仅仅被解码并解析为标识符名称或者字符串常量
所以这样是不行的
<input type="button" name="demo" onclick="\u0061\u006c\u0065\u0072\u0074u0028\u0031\u0029">
(alert(1)的转码)总的来说,Unicode转义序列只有在标识符名称里不被当作字符串,也只有在标识符名称里的编码字符能够被正常的解析
解析顺序以及一些细节
解析顺序一般是html首先进行解析,javascript和url的解析顺序视情况而定。
比如:
A中先html解析,然后进行url解析,如果userinput是js代码最后进行js解析。而B中则是先html解析,然后js解析,最后url解析
注意某些dom操作会导致html从新解析。
防御
chrome浏览器自带防御,可拦截反射性XSS(HTML内容和属性),js和富文本的无法拦截,所以我们必须得自己做一些防御手段。
escape
CSP(Content Security Policy)
内容安全策略(Content Security Policy,简称CSP)是一种以可信白名单作机制,来限制网站中是否可以包含某来源内容。默认配置下不允许执行内联代码( Githubissues.