# Web 前端安全,如何防止 XSS 攻击
随着互联网的高速发展
信息安全问题已经成为企业最为关注的焦点之一,而前端又是引发企业安全问题的高危据点。
- 在移动互联网时代,前端人员除了传统的 XSS、CSRF 等安全问题之外,又时常遭遇网络劫持、非法调用 Hybrid API 等新型安全问题。
- 当然,浏览器自身也在不断在进化和发展,不断引入 CSP、Same-Site Cookies 等新技术来增强安全性,但是仍存在很多潜在的威胁,这需要前端技术人员不断进行“查漏补缺”。
近几年,随着业务高速发展,前端随之面临很多安全挑战,因此积累了大量的实践经验。
我们梳理了常见的前端安全问题以及对应的解决方案,将会做成一个系列,希望可以帮助前端人员在日常开发中不断预防和修复安全漏洞。
# 1、什么是 XSS ?
XSS 解析
- Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。
- 攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。
- 利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
为了和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS。
# 2、XSS 的本质
本质
- 恶意代码未经过滤,与网站正常的代码混在一起;
- 浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
- 而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。
# 3、用户是通过哪种方法 “注入” 恶意脚本的呢 ?
注入恶意脚本的方式
不仅仅是业务上的“用户的 UGC 内容”可以进行注入,还包括 URL 上的参数等都可以是攻击的来源。在处理输入时,以下内容都不可信:
- 来自用户的 UGC 信息,UGC(称为 User Generated Content,也就是用户生成内容,即用户原创内容)
- 来自第三方的链接
- URL 参数
- POST 参数
- Referer (可能来自不可信的来源)
- Cookie (可能来自其他子域注入)
# 4、据攻击的来源,XSS 攻击可分为存储型、反射型和 DOM 型三种
XSS 攻击的 3 种类型
类型 | 存储区 | 插入点 |
---|---|---|
存储型 XSS | 后端数据库 | HTML |
反射型 XSS | URL | HTML |
DOM 型 XSS | 后端数据库/前端存储/URL | 前端 JavaScript |
- 存储区:恶意代码存放的位置。
- 插入点:由谁取得恶意代码,并插入到网页上。
# (1)存储型 XSS 的攻击步骤:
- ① 攻击者将恶意代码提交到目标网站的数据库中。
- ② 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- ③ 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- ④ 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
# (2)反射型 XSS 的攻击步骤:
- ① 攻击者构造出特殊的 URL,其中包含恶意代码。
- ② 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- ③ 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- ④ 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 与 存储型 XSS 的区别是:
- 存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
- 反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
- 由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
- POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。
# (3)DOM 型 XSS 的攻击步骤:
- ① 攻击者构造出特殊的 URL,其中包含恶意代码。
- ② 用户打开带有恶意代码的 URL。
- ③ 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- ④ 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 与 前两种 XSS 的区别:
DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
# 5、系统总结 XSS 有哪些注入的方法 ?
总结
- 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入
- 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)
- 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签
- 在标签的 href、src 等属性中,包含
javascript:
等可执行代码 - 在 onload、onerror、onclick 等事件中,注入不受控制代码
- 在 style 属性和标签中,包含类似
background-image:url("javascript:...");
的代码(新版本浏览器已经可以防范) - 在 style 属性和标签中,包含类似
expression(...)
的 CSS 表达式代码(新版本浏览器已经可以防范)
总之,如果开发者没有将用户输入的文本进行合适的过滤,就贸然插入到 HTML 中,这很容易造成注入漏洞。攻击者可以利用漏洞,构造出恶意的代码指令,进而利用恶意代码危害数据安全。
# 6、系统总结:XSS 攻击防范措施
总结
整体的 XSS 防范是非常复杂和繁琐的,我们不仅需要在全部需要转义的位置,对数据进行对应的转义。而且要防止多余和错误的转义,避免正常的用户输入出现乱码。
虽然很难通过技术手段完全避免 XSS,但我们可以总结以下原则减少漏洞的产生:
利用模板引擎 开启模板引擎自带的 HTML 转义功能。例如:
- 在 ejs 中,尽量使用
<%= data %>
而不是<%- data %>
; - 在 doT.js 中,尽量使用
{{! data }
而不是{{= data }
; - 在 FreeMarker 中,确保引擎版本高于 2.3.24,并且选择正确的
freemarker.core.OutputFormat
。
- 在 ejs 中,尽量使用
避免内联事件 尽量不要使用
onLoad="onload('')"
、onClick="go('')"
这种拼接内联事件的写法。在 JavaScript 中通过.addEventlistener()
事件绑定会更安全。避免拼接 HTML 前端采用拼接 HTML 的方法比较危险,如果框架允许,使用
createElement
、setAttribute
之类的方法实现。或者采用比较成熟的渲染框架,如 Vue/React 等。
时刻保持警惕 在插入位置为 DOM 属性、链接等位置时,要打起精神,严加防范。
增加攻击难度,降低攻击后果 通过 CSP、输入长度配置、接口安全措施等方法,增加攻击的难度,降低攻击的后果。
主动检测和发现 可使用 XSS 攻击字符串和自动扫描工具寻找潜在的 XSS 漏洞。
基础防范措施:
- ①、对富文本编辑器,过滤 script 等不安全标签
- ②、对用户输入进行转义,比如把
<script></script>
转义为<
script>
<
/script>
- ③、代码需要动态展示内容时用 innerText 取代 innerHTML ,使用 v-text 取代 v-html,尽量少做 HTML 字符串拼接,禁止使用 eval 执行 JS。
- ④、服务端 Set Cookie 时 带上 HttpOnly 字段,阻止 JavaScript 获取 Cookie
- ⑤、对于上传图片的场景,禁止使用用户填写的图片地址。特别是 Markdown 编辑器。
# (1)预防 DOM 型 XSS 攻击
DOM 型 XSS 攻击
实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。
在使用
.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,很容易产生安全隐患,请务必避免。
# (2)Content Security Policy (内容安全策略)
严格的 CSP 在 XSS 的防范中可以起到以下的作用:
- 禁止加载外域代码,防止复杂的攻击逻辑。
- 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
- 禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
- 禁止未授权的脚本执行(新特性,Google Map 移动版在使用)。
- 合理使用上报可以及时发现 XSS,利于尽快修复问题。
# (3)输入内容长度控制
TIP
- 对于不受信任的输入,都应该限定一个合理的长度。
- 虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度。
# (4)其他安全措施
TIP
- HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
- 验证码:防止脚本冒充用户提交危险操作。
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X