手把手教你快速上手XSS攻击

本文引用部分内容引用自# 前端安全系列(一):如何防止XSS攻击?

XSS攻击介绍

跨站脚本攻击(Cross-site scripting,XSS)是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。若受害者运行这些恶意代码,攻击者就可以突破网站的访问限制并冒充受害者。根据开放式 Web 应用安全项目(OWASP),XSS 在 2017 年被认为 7 种最常见的 Web 应用程序漏洞之一

XSS 攻击可以分为 3 类:存储型(持久型)、反射型(非持久型)、DOM 型。

  • 存储型 XSS

    注入型脚本永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器上传回并执行。

  • 反射型 XSS

    当用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。Web 服务器将注入脚本,比如一个错误信息,搜索结果等 返回到用户的浏览器上。由于浏览器认为这个响应来自"可信任"的服务器,所以会执行这段脚本。

  • 基于 DOM 的 XSS

    通过修改原始的客户端代码,受害者浏览器的 DOM 环境改变,导致有效载荷的执行。也就是说,页面本身并没有变化,但由于 DOM 环境被恶意修改,有客户端代码被包含进了页面,并且意外执行。

XSS常见注入方法

  • 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。

  • 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。

  • 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。

  • 在标签的 href、src 等属性中,包含 javascript: (伪协议)等可执行代码。

  • 在 onload、onerror、onclick 等事件中,注入不受控制代码。

  • 在 style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)。

  • 在 style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)。

反射型XSS

恶意 JavaScript 脚本属于用户发送给网站请求中的一部分,随后网站又将这部分返回给用户,恶意脚本在页面中被执行。一般发生在前后端一体的应用中,服务端逻辑会改变最终的网页代码。

简单概括,就是前端发送请求链接,后端将请求中的参数拼接到HTML中返回。而XSS攻击使得逻辑跳出了预先准备好的框架,使其以不符合预期的行为运行

攻击步骤

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

存储型XSS

黑客将恶意 JavaScript 脚本长期保存在服务端数据库中,用户一旦访问相关页面数据,恶意脚本就会被执行。常见于搜索、微博、社区贴吧评论等。

存储型 XSS 的攻击步骤:

  1. 攻击者将恶意代码提交到目标网站的数据库中。
  2. 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

DOM型XSS攻击

DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。

在使用 .innerHTML.outerHTMLdocument.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent.setAttribute()

DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。

在使用 .innerHTML.outerHTMLdocument.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent.setAttribute()

DOM型XSS的攻击步骤

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL。
  3. 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

攻击案例

script标签注入

案例

html 复制代码
<input type="text" value="getParameter("keyword")">
<button>搜索</button>
<div>
  您搜索的关键词是: {{getParameter("keyword")}}
</div>

倘若此时有一个请求为:

http://xxx/search?keyword=">script>alert('XSS');</script>

点击该请求,会发现代码将">script>alert('XSS');</script>拼接到了html中,此时html为,运行后浏览器将会执行script内代码

html 复制代码
<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
  您搜索的关键词是:"><script>alert('XSS');</script>
</div>

解决方案

此时我们可以通过转义html关键字符来避免script标签插入,例如利用escapeHTML()

字符 转义后的字符
& &amp;
< &lt;
> &gt;
" &quot;
' &#x27;
/ &#x2F;

注意,尽量使用业界成熟的转义方案

HTML属性更改,特殊JavaScript API

案例

html 复制代码
<a href="escapeHTML(getParameter('redirect_to'))">跳转...</a>

倘若此时有一个请求:

http://xxx/?redirect_to=javascript:alert('XSS')

此时url参数redirect_to会被拼接到a标签的href属性中,当用户点击改标签时,就会触发javascript脚本

html 复制代码
<a href="javascript:alert(&#x27;XSS&#x27;)">跳转...</a>

解决方案

遇到这种类似的情景,必须要对用户的输入进行校验,禁止以javascript:开头的链接,或其他非法的scheme

XSS攻击的预防

根据上文的介绍,我们可以讲XSS攻击分成两部分

  1. 攻击者提交恶意代码
  2. 浏览器执行恶意代码

根据这两个部分我们可以进行针对性的预防

输入过滤

在进行输入过滤时,时机是一个值得思考的问题,如果由前端执行,那么很有可能攻击者会直接绕过前端过滤,直接构造请求,这样就可以提交代码了。

那如果是后端过滤呢?后端在写入数据库前,对输入进行过滤,然后把内容返回给前端。但需要考虑的问题是:前端何时进行转义

  • 后端存储的内容也可能会发往客户端,而经过HTML的转义,客户端无法正确显示字符
  • 即使是前端,在不同的地方所需的编码也不同

因此,输入过滤很多情况只能用于明确的输入类型,例如数字,URL,电话号码,邮件地址等等内容

预防存储型和反射型XSS攻击

让我们回忆一下存储型和反射型XSS攻击,这两个攻击都是攻击者通过将恶意代码提交到服务器,由服务器存储或拼接html后返回结果,而这个结果就包含了攻击者恶意写入的内容

预防这两种漏洞,通常有两种常见的做法:

  • 改成纯前端渲染,将代码和数据分隔
  • 对HTML进行充分的转义

纯前端渲染

纯前端渲染的过程:

  1. 浏览器先加载一个静态 HTML,此 HTML 中不包含任何跟业务相关的数据。
  2. 然后浏览器执行 HTML 中的 JavaScript。
  3. JavaScript 通过 Ajax 加载业务数据,调用 DOM API 更新到页面上。

在纯前端渲染中,我们会明确的告诉浏览器:下面要设置的内容是文本(.innerText),还是属性(.setAttribute),还是样式(.style)等等。浏览器不会被轻易的被欺骗,执行预期外的代码了。

但纯前端渲染还需注意避免 DOM 型 XSS 漏洞(例如 onload 事件和 href 中的 javascript:xxx 等,请参考下文"预防 DOM 型 XSS 攻击"部分)。

在很多内部、管理系统中,采用纯前端渲染是非常合适的。但对于性能要求高,或有 SEO 需求的页面,我们仍然要面对拼接 HTML 的问题。

转义HTML

很多时候我们无法避免拼接HTML,此时我们就需要严格的转义规则去避免浏览器执行额外的内容。

通常我们会采用一些合适的转义库,从而对HTML模版各处进行充分的转义

预防DOM型XSS攻击

DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。

在使用 .innerHTML.outerHTMLdocument.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent.setAttribute() 等。

如果用 Vue/React 技术栈,并且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 阶段避免 innerHTMLouterHTML 的 XSS 隐患。

DOM 中的内联事件监听器,如 locationonclickonerroronloadonmouseover 等,<a> 标签的 href 属性,JavaScript 的 eval()setTimeout()setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。

使用Content Security Policy

内容安全策略CSP)是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本(XSS)和数据注入攻击等。无论是数据盗取、网站内容污染还是恶意软件分发,这些攻击都是主要的手段。

启用

配置网络服务器返回 Content-Securiry-Policy HTTP标头

此外,<meta>元素也可以被用来配置改策略

html 复制代码
<meta
  http-equiv="Content-Security-Policy"
  content="default-src 'self'; img-src https://*; child-src 'none';" />

作用

严格的CSP在XSS的防范中可以起到以下作用

  • 禁止加载外域代码,防止复杂的攻击逻辑。
  • 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
  • 禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
  • 禁止未授权的脚本执行(新特性,Google Map 移动版在使用)。
  • 合理使用上报可以及时发现 XSS,利于尽快修复问题。

输入内容长度控制

对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度。

其他安全措施

  • HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
  • 验证码:防止脚本冒充用户提交危险操作。
相关推荐
鑫宝Code7 分钟前
【React】React Router:深入理解前端路由的工作原理
前端·react.js·前端框架
Daniel 大东1 小时前
BugJson因为json格式问题OOM怎么办
java·安全
Mr_Xuhhh1 小时前
重生之我在学环境变量
linux·运维·服务器·前端·chrome·算法
永乐春秋2 小时前
WEB攻防-通用漏洞&文件上传&js验证&mime&user.ini&语言特性
前端
鸽鸽程序猿2 小时前
【前端】CSS
前端·css
ggdpzhk2 小时前
VUE:基于MVVN的前端js框架
前端·javascript·vue.js
学不会•4 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNVR6 小时前
NVR管理平台EasyNVR多个NVR同时管理:全方位安防监控视频融合云平台方案
安全·音视频·监控·视频监控
活宝小娜7 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点7 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript