likes
comments
collection
share

【译】了解 XSS 攻击

作者站长头像
站长
· 阅读数 89

了解 XSS 攻击

跨站脚本 (XSS) 是一种攻击,攻击者将恶意脚本注入其他用户查看的网页中。这些脚本可以绕过同源策略,允许攻击者窃取敏感数据、劫持用户会话、破坏网站或传播恶意软件

想象一下,您正在博客上阅读评论部分。您几乎不知道此评论包含一个隐藏脚本,该脚本在执行时会窃取您的会话 cookie。哎呀,你现在是 XSS 攻击的受害者。但这是怎么发生的呢?

XSS 攻击是如何发生的

XSS 攻击分几个阶段发生:

1. 攻击者识别出容易受到 XSS 攻击的目标站点。如果网站盲目信任用户输入并且在呈现之前没有对其进行清理,则该网站容易受到攻击。

【译】了解 XSS 攻击

2. 攻击者制作恶意脚本。这可能是恶意脚本或损坏的图像,用于捕获用户的会话 cookie 并将其发送到攻击者的服务器。

3. 攻击者将此脚本嵌入到易受攻击的站点中。他们可以将脚本发布为评论、个人资料状态或将向其他用户显示的任何其他用户生成的内容。在这种情况下,攻击者嵌入一个调用其 onerror 属性的错误图像,该图像将包含用户 cookie 的请求发送到攻击者的服务器。

【译】了解 XSS 攻击

4. 该脚本在易受攻击站点的用户浏览器中运行。如果没有足够的安全措施,此恶意脚本可能会访问浏览器为该站点保留的任何 Cookie、会话令牌或其他敏感信息,并将其发送给攻击者。

XSS 攻击的类型

XSS 攻击有三种类型:DOM 型(DOM-based)、反射型(reflected)和存储型(stored)。

1. DOM型 XSS

当由于使用客户端 JavaScript “即时”修改 DOM 而执行恶意有效载荷时,就会发生这种类型的 XSS 攻击。

基于 DOMXSS 的独特之处在于它完全在客户端运行,无需向服务器发送 HTTP 请求。攻击通过操纵 DOM 在受害者的浏览器中展开。客户端执行使这些攻击更难检测,因为传统的安全措施通常侧重于服务器流量。对于攻击者来说,这需要对 JavaScriptWeb 应用程序的结构有细致入微的理解才能成功利用,使其成为一个更微妙但具有挑战性的攻击媒介。

想象一个简单的网页,它读取一个 URL 参数并使用 JavaScript 更新页面的内容:

<div id="content">Default content</div>
<script>
  const params = new URLSearchParams(window.location.search)
  document.getElementById('content').innerHTML = params.get('message')
</script>

如果用户访问类似 http://example.com/index.html?message=<script>alert('Hacked!')</script> 的链接,恶意脚本将运行。

2. 反射型 XSS

当攻击者提供的恶意脚本从 Web 服务器反射出来(例如通过搜索结果或错误消息)并立即执行而不进行存储时,就会发生此攻击

反射式 XSS 通常需要某种形式的社会工程(social engineering),因为必须诱骗受害者单击包含恶意载荷的特制链接。单击链接后,有效载荷将发送到易受攻击的网站,然后将攻击反射回用户的浏览器,在那里执行恶意脚本。由于其非持久性,攻击是实时发生的,不会影响网站的其他用户

想象一下,网站上有一个简单的搜索功能,可以向用户显示搜索查询。如果网站在将其内容反映回页面上之前未能正确清理 query 参数,则脚本将执行,从而导致 XSS 攻击。

<div>
  <!-- Vulnerable Code: Reflects the user input directly without sanitization -->
  <!-- 易受攻击的代码:直接反映用户输入而不进行清理 -->
  You searched for: <span id="search-result"></span>
</div>

<script>
  // Get the query parameter from the URL
  const params = new URLSearchParams(window.location.search)
  const query = params.get('query')

  // Reflect the query directly into the page (vulnerable!)
  // 将查询直接反映到页面中(易受攻击!)
  document.getElementById('search-result').innerHTML = query
</script>

如果用户输入脚本而不是 ID,它将在浏览器上执行,从而导致反射 XSS 攻击。

3. 存储型 XSS

这是最危险的 XSS 攻击类型。在这里,恶意脚本通常通过表单或类似的输入机制直接注入网站的存储中。当其他用户访问此网站时,他们会在不知不觉中从网站数据库加载并执行恶意脚本,使其在会话中持续存在。

考虑博客上的评论部分:

<textarea id="comment"></textarea>
<button onclick="postComment()">Post Comment</button>
<div id="commentsSection"></div>
<script>
  function postComment() {
    const comment = document.getElementById('comment').value
    // Imagine this gets stored in a database without validation.
    saveCommentToDB(comment)
    document.getElementById('commentsSection').innerHTML += comment
  }

  function loadComments() {
    // Loads comments from db without validation
    const comments = getCommentsFromDB()
    document.getElementById('commentsSection').innerHTML = comments
  }
</script>

用户可以将脚本作为评论发布。当另一个用户访问该页面时,将执行存储在数据库中的脚本。

XSS 攻击可能会对您的用户和产品产生毁灭性影响。

  1. 他们秘密运作,隐藏在合法网站内。这是 XSS 攻击如此有效和危险的主要原因之一。用户认为他们正在与受信任的网站进行交互,可能会无意中触发恶意脚本,认为这是网站常规操作的一部分。

  2. 任何查看感染了 XSS 有效载荷的页面或 Web 应用程序的人都处于危险之中。这使得此类攻击对网络犯罪分子来说具有难以置信的可扩展性。例如,如果攻击者将恶意脚本注入热门网站的评论部分,则查看该评论的任何用户都可能成为受害者。这种可扩展性凸显了强大的网络安全措施对于吸引大量访问者的热门网站的重要性。

  3. XSS 攻击的后果是多方面的,可能会产生重大影响。这包括通过 Cookie、会话令牌或其他敏感信息窃取数据,通过强制用户浏览器下载和运行恶意软件来分发恶意软件,以及通过诱骗用户提供个人数据或登录凭据进行网络钓鱼。

  4. XSS 攻击可能会破坏用户对您产品的信任

防止 XSS 攻击

XSS 的保护需要尽职尽责,但有一些有效的方法可以缓解它们。

拒绝意外输入

输入应与预期格式匹配;例如,年龄字段应仅包含数字。

// app/api/route.js
export function POST(req)  {
    const { age }  = req.body;

    if (!Number.isInteger(Number(age))) {
      res.status(400).send('Invalid age input. Please enter a valid number.');
      return;
    }

    res.status(200).send(`Your age is: ${age}`);
  } else {
    res.status(405).end(); // Method Not Allowed
  }
};

验证和清理用户输入

使用 DOMPurify 等库来验证和清理用户输入,确保在呈现之前清除恶意脚本。

// app/api/route.js
import { sanitize } from "isomorphic-dompurify";

export function POST(req) {
  const { comment } = req.body;
  const sanitizedComment = sanitize(comment);
  ...
}

避免使用内联脚本和内联事件处理程序

相反,请使用外部脚本并动态绑定事件。这样可以减少意外执行攻击者控制的脚本的机会。

<!-- Inline Event (Not Recommended) -->
<button onclick="alert('Button was clicked!')">Click me!</button>

----

<!-- External Event (Recommended) -->
<button id="myButton">Click me!</button>

<script>
  document.getElementById('myButton').addEventListener('click', function () {
    alert('Button was clicked!')
  })
</script>

设置内容安全策略 (CSP)

CSP 是一种浏览器功能,可帮助降低 XSS 攻击的风险。它允许网站指定允许哪些来源,阻止任何恶意或意外内容。

<!-- This CSP allows only scripts loaded from the current site's origin -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self';" />

Next.js 中,可以使用中间件配置 CSP 标头

(React) 避免使用 dangerouslySetInnerHTML

此属性允许将 HTML 内容直接插入到 React 应用程序中。如果内容包含恶意脚本,并且未进行适当清理,则这些脚本可能会在用户的浏览器中执行。如果必须使用它,请先对内容进行消毒!默认情况下,React 的 JSX 会转义所有变量。利用此功能并避免手动创建 DOM 内容。

import React from 'react'

// Imagine this comment is fetched from a user or an external source
const userComment = '<img src=x onerror=alert("XSS Attack")>'

export function BadComment() {
  // ❌ No sanitization on dangerouslySetInnerHTML
  return <div dangerouslySetInnerHTML={{ __html: userComment }}></div>
}

export function BetterComment() {
  const sanitizedComment = DOMPurify.sanitize(userComment)

  // ⚠️ Still using dangerouslySetInnerHTML, but sanitized
  return <div dangerouslySetInnerHTML={{ __html: sanitizedComment }}></div>
}

export function BestComment() {
  // ✅ React automatically escapes content within curly brackets
  return <div>{userComment}</div>
}

在 Cookie 上使用 HttpOnly and Secure 属性

通过在 cookie 上设置 HttpOnly 标志,您可以防止 JavaScript 访问它。该 Secure 标志确保 cookie 只能通过安全的 HTTPS 连接传输。在此处了解有关 Cookie 的更多信息。

选择服务器端输入验证而不是客户端

客户端验证主要用于用户体验。虽然它提供快速反馈,但它很容易被操纵。如果服务器盲目信任此输入,恶意行为者可以规避客户端检查,从而导致 XSS 漏洞。

服务器端验证是抵御 XSS 的最后一道防线。通过验证和清理服务器上的输入,即使恶意输入通过了客户端防御,也可以防止执行不需要的脚本。

在使用或显示之前,必须在服务器上验证和清理所有数据。若要减少安全漏洞,请始终确保客户端和服务器上的验证规则和技术一致。

谨慎使用第三方库

确保你包含的任何第三方 React 组件或Next.js插件不会引入 XSS 漏洞。

Conclusion 结论

XSS 攻击可能会造成重大损害,但可以通过适当的预防措施完全避免。开发人员应将安全放在首位,并不断更新他们的知识,以便随时了解新出现的威胁和保护机制。确保 Web 应用程序的安全性是一个持续的过程,对 XSS 等威胁保持警惕至关重要。

Further Reading 延伸阅读

原文链接

转载自:https://juejin.cn/post/7378750771414548534
评论
请登录