XSS介绍
Cross-Site Scripting(跨站脚本攻击)简称XSS,是一种代码注入攻击。为了和CSS区分,这里将其缩写的第一个字母改成了X,于是叫做XSS。恶意攻击者在web页面里插入恶意JavaScript代码,当浏览者浏览网页时就会触发恶意代码,从而导致用户数据的泄露如Cookie、SessionID等,及造成其他类型的攻击。
XSS的本质是:恶意代码未经过滤,与网站正常代码混在一起,浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。从源头上讲,就是代码和数据被攻击混淆,导致浏览器将攻击者输入的数据解释为代码。
案例介绍
对于下面的代码
<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= getParameter("keyword") %>
</div>
当构造链接:
http://xxx/search?keyword="><script>alert('XSS');</script>
浏览器请求该链接时,服务端会解析出参数keyword,得到"><script>alert('XSS');</script>
,拼接后,得到:
<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
您搜索的关键词是:"><script>alert('XSS');</script>
</div>
因为参数中的数据和页面代码产生了混淆,浏览将用户参数中的<script>alert('XSS');</script>
,解析为代码执行。
修复这个问题可以对用户输入参数进行转义。
使用函数escapeHTML()
<input type="text" value="<%= escapeHTML(getParameter("keyword")) %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= escapeHTML(getParameter("keyword")) %>
</div>
escapeHTML()
的转义规则为:
字符 | 转义后的字符 |
---|---|
& |
& |
< |
< |
> |
> |
" |
" |
' |
' |
/ |
/ |
经过转义,浏览器就可以很好的区分用户提交的数据和页面中的代码。
<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
您搜索的关键词是:"><script>alert('XSS');</script>
</div>
- 用户输入的为数据,目的是以文本显示出来
- 攻击者利用输入精心构造的片段,导致代码和数据混淆,使得浏览器将用户输入解析为代码
- 通过HTML转移可以防止XSS攻击
有时仅仅做HTML转义也是不够的。如:如果将JSON内联在HTML中
<script>
var initData = <%= data.toJSON() %>
</script>
这时就不能使用escapeHTML()
,因为会破坏JSON格式。
但此时并不安全
- 当 JSON 中包含
U+2028
或U+2029
这两个字符时,不能作为 JavaScript 的字面量使用,否则会抛出语法错误。 - 当 JSON 中包含字符串
</script>
时,当前的 script 标签将会被闭合,后面的字符串内容浏览器会按照 HTML 进行解析;通过增加下一个<script>
标签等方法就可以完成注入。
所以我们要丢此种情景单独进行转义。
字符 | 转义后的字符 |
---|---|
U+2028 | \u2028 |
U+2029 | \u2029 |
< | \u003c |
根据不同的场景需要采用不同的转义规则
避免自己写转义库,可能出现考虑不到的地方,尽量使用成熟、通用的转义库
有时就算是做了所有的转义,但是还是可能存在XSS,如对于下面代码:
<a href="<%= escapeHTML(getParameter("redirect_to")) %>">跳转...</a>
构造链接:
http://xxx/?redirect_to=javascript:alert('XSS')
此时,服务端的响应为:
<a href="javascript:alert('XSS')">跳转...</a>
在点击a
标签时,实现了XSS。
因在在于用户输入的并不是我们预期的数据。这里可以采取白名单策略,过滤非期望输入。
对用户输入数据做要求限制,过滤非期望的输入
做黑名单要考虑完全,如果考虑不周,可能会被绕过。如,上面的例子,如果仅仅过滤
JavaScript
,那么利用大小写就可以绕过。
XSS攻击分类
XSS攻击可以分为三类,反射性、存储型和DOM型。
类型 | 存储区 | 插入点 |
---|---|---|
存储型 XSS | 后端数据库 | HTML |
反射型 XSS | URL | HTML |
DOM 型 XSS | 后端数据库/前端存储/URL | 前端 JavaScript |
存储区:恶意代码存放位置
插入点:由谁取得恶意代码,并插入网页
反射型XSS
攻击步骤:
- 攻击者构造特殊的URL,其中包含了攻击者的恶意代码
- 攻击者将构造URL发送给用户。用户访问URL,服务端将恶意代码拼接在HTML中返回给浏览器
- 用户浏览器接收响应后解析执行,攻击者构造的恶意代码被当作代码执行
- 恶意代码窃取用户数据发送给攻击者,或者执行特定操作
攻击常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
POST请求也可以触发反射型XSS,但触发条件苛刻,较少见。
存储型XSS
攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
DOM型XSS
攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
XSS注入点
HTML节点内容
如果用户在评论去输入
"评论“
<script>alert("XSS")</script>
如果没有任何防御措施,最终会解析为:
<div class = "commentContent">
"评论“
<script>alert("XSS")</script>
</div>
HTML属性/参数
如上面说到的
<a href="<%= escapeHTML(getParameter("redirect_to")) %>">跳转...</a>
当用户输入的地址为:
http://xxx/?redirect_to=javascript:alert('XSS')
就会构成XSS。
JavaScript代码
如果用户输入为JavaScript中的变量,则可以被精心构造,导致字符串提前关闭,实现注入。
<script> var $a = "";alert("XSS");"";</scaript>
富文本
富文本的本质就是一段HTML,那么就可能存在XSS攻击。并且富文本攻击的防御相对比较麻烦。
XSS绕过
为了更好的做好XSS的防御,我们在之前防御规则的基础上,看一下常用的绕过姿势,看你的防御规则是不是容易被绕过。
-
大小写绕过
如果在过滤时考虑不周,仅仅过滤固定的标签,但浏览器是不区分大小写的,所以攻击者可以通过改变大小写来绕过。
-
利用过滤后返回的语句绕过
这个绕过的思想是过滤语句会将关键词去除,但是如果考虑步骤仅仅去除一次,则可以对管检测进行嵌套,如<Scr<script>iPt>alert("XSS")</sC</script>>ript>,过滤一次关键词时,返回值正好时指定的恶意代码。
-
利用除script之外的标签
不仅script标签可以实现XSS,其他标签也是可以的。如<img>,<a>等
-
将关键词编码绕过
如果防御规则是将关键词过滤,可以将关键词进行编码,来绕过规则,然后利用eval()来执行。
如alert(1)编码后是
\u0061\u006c\u0065\u0072\u0074(1)
组合方式
XSS危害
-
窃取用户信息
最常见的就是窃取用户的cookie。窃取cookie后,攻击者就可能能够直接利用令牌而不用密码就登录用户账户。
劫持流量实现恶意跳转
控制受害者机器向其它网站发起攻击
强制发送电子邮件
控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力
XSS攻击防御
XSS攻击的两个前提:
- 攻击者提交恶意代码
- 浏览器执行恶意代码
过滤
对诸如<script>、<img>、<a>
等标签进行过滤。
转义
转义时可以考虑两个方面,前端和后端。
如果仅仅在前端转义,如果攻击者可以绕过前端,直接构造请求,那么就可以提交恶意代码。
如果在后端转义,在写入数据库之前将用户的数据进行转义存储,使用时再取出来。
但是后端可能会导致内容输出时出现问题,如
-
如果用户输入内容需要同时提供给前端和客户端,经过转义后的数据并不能同时适应两端的要求。
5<7
经过转义为5<7
,此时直接在客户端输出会显示乱码 -
在前端不同位置,也需要不同的编码。
在HTML页面中显示;
赋值给JavaScript变量;
转义可以解决某种情况下的XSS,但是会引入不确定性问题。
这里就需要前后端协调,确保我明确知道获得的数据是否做过转义,我需要的数据是什么样的。
同时在转义时要采用合适的转义库,在HTML模板各处插入点进行充分的转义。
XSS 安全漏洞 | 简单转义是否有防护作用 |
---|---|
HTML 标签文字内容 | 有 |
HTML 属性值 | 有 |
CSS 内联样式 | 无 |
内联 JavaScript | 无 |
内联 JSON | 无 |
跳转链接 | 无 |
代码数据分离
前端为纯渲染过程:
- 浏览器先加载一个静态 HTML,此 HTML 中不包含任何跟业务相关的数据。
- 然后浏览器执行 HTML 中的 JavaScript。
- JavaScript 通过 Ajax 加载业务数据,调用 DOM API 更新到页面上。
在纯前端渲染中,浏览器可以明确知道要设置的内容是文本(.innerText
),还是属性(.setAttribute
),还是样式(.style
)等等。
注意前端代码的严谨性
为避免DOM型XSS,在使用 .innerHTML
、.outerHTML
、document.write()
时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent
、.setAttribute()
等。
如果用 Vue/React 技术栈,并且不使用 v-html
/dangerouslySetInnerHTML
功能,就在前端 render 阶段避免 innerHTML
、outerHTML
的 XSS 隐患。
DOM 中的内联事件监听器,如 location
、onclick
、onerror
、onload
、onmouseover
等,<a>
标签的 href
属性,JavaScript 的 eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,尽量避免使用。
CSP策略
Content Security Policy(安全内容策略)是一种以可信白名单作机制,来限制网站中是否可以包含某来源内容。
默认配置下不允许执行内联代码(<script>
块内容,内联事件,内联样式),以及禁止执行eval() , newFunction() , setTimeout([string], ...) 和setInterval([string], ...) 。
- 禁止加载外域代码,防止复杂的攻击逻辑。
- 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
- 禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
- 禁止未授权的脚本执行(新特性,Google Map 移动版在使用)。
- 合理使用上报可以及时发现 XSS,利于尽快修复问题。
输入内容长度控制
xss攻击要能达成往往需要较长的字符串,因此对于一些可以预期的输入可以通过限制长度强制截断来进行防御。
HTTP-only Cookie
禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
XSS 的检测
在Unleashing an Ultimate XSS Polyglot一文中,有一个字符串:
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e
它能够检测到存在于 HTML 属性、HTML 文字内容、HTML 注释、跳转链接、内联 JavaScript 字符串、内联 CSS 样式表等多种上下文中的 XSS 漏洞,也能检测 eval()
、setTimeout()
、setInterval()
、Function()
、innerHTML
、document.write()
等 DOM 型 XSS 漏洞,并且能绕过一些 XSS 过滤器。
只要在网站的各输入框中提交这个字符串,或者把它拼接到 URL 参数上,就可以进行检测。
检测工具:
引用
原文地址:渗透技术基础之XSS原理分析