深入解析XSS攻击:从原理到防御的全方位指南

XSS

一、什么是XSS?

  1. Cross-Site Scripting(跨站脚本攻击)为了与CSS作区分故简称 XSS,是一种代码注入攻击 。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
  2. 本质
    • 恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
  3. 可能被注入恶意脚本的内容
    • 来自用户的 UGC 信息
    • 来自第三方的链接
    • URL 参数
    • POST 参数
    • Referer (可能来自不可信的来源)
    • Cookie (可能来自其他子域注入)

二、XSS攻击方式

  1. 分为三种:存储型 、反射型 、DOM 型

存储型XSS
  • 不需要用户手动触发,所有浏览者访问页面时都会被XSS
  • 常见于带有用户保存数据 的网站功能,如论坛发帖、商品评论、用户私信等
反射型XSS
  • 需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击
  • 常见于通过 URL 传递参数 的功能,如网站搜索、跳转等
DOM型XSS
  • 属于前端 JavaScript 自身的安全漏洞
区别
特征 存储型 XSS 反射型 XSS DOM 型 XSS
存储位置 恶意代码存储在服务器数据库 恶意代码不存储,仅存在于URL参数 恶意代码不存储,存在于URL片段
触发条件 用户访问被感染的页面 用户点击包含恶意参数的链接 用户点击包含恶意片段(#)的链接
数据流 服务器 → 用户浏览器 服务器反射参数 → 用户浏览器 完全在客户端处理(不经过服务器)
持久性 长期存在(直到数据被删除) 一次性(仅当用户点击链接时) 一次性(仅当用户点击链接时)
攻击范围 所有访问该页面的用户 仅点击特定链接的用户 仅点击特定链接的用户

三、常见的XSS攻击场景

社交媒体评论------存储型XSS
  • 攻击者在评论区提交恶意脚本(如 <script>sendCookiesToAttacker()</script>),该评论被存储到服务器 。其他用户访问该页面时,恶意脚本自动加载并窃取其会话Cookie。
html 复制代码
<!-- 用户评论内容 -->
<div class="comment">
  这篇文章写得真好!<script>sendCookiesToAttacker()</script>
</div>
搜索功能------反射型XSS
  • 网站搜索功能未过滤输入参数,攻击者构造恶意链接:https://example.com/search?q=<script>alert('XSS攻击')</script>,用户点击链接 后,服务器返回的页面包含未过滤的恶意脚本。
单页面应用路由------DOM型XSS
  • SPA前端路由根据URL参数动态加载内容,但未对参数过滤。攻击者构造链接:https://example.com/#/profile?username=<script>alert('XSS攻击')</script>,用户点击链接前端脚本username 参数直接渲染到页面,触发XSS。

四、防御策略

预防存储型XSS

复制代码
 ##### 输入过滤

 * 对于**明确的输入类型**

   * 如数字、URL、电话号码、邮件,使用`escapeHTML()`可以把用户的输入内容进行转义

   | 原始字符 | 转义后实体编码 |
   |:-----|:--------|
   | `<`  | `&lt`   |
   | `>`  | `&gt`   |
   | `&`  | `&amp`  |
   | `"`  | `&quot` |
   | `'`  | `&#x27` |

 ```js
 // 用户输入:<script>alert('XSS攻击')</script>
 const escaped = escapeHTML(userInput);
 document.getElementById('content').innerHTML = escaped;
 // 输出:&lt;script&gt;alert('XSS攻击')&lt;/script&gt;
 ```

 * 更推荐使用成熟且完善的**现有库**

 ```js
 // 使用 DOMPurify(支持更复杂的净化)
 import DOMPurify from 'dompurify';
 DOMPurify.sanitize(userInput, { ALLOWED_TAGS: [] }); 
 ```

 * **特殊场景**补充

 | 场景     | 额外需要转义的字符         | 方法                        |
 |:-------|:------------------|:--------------------------|
 | URL参数  | `%` `#`           | 配合使用 `encodeURIComponent` |
 | CSS属性  | `\` `()`          | 使用 `CSS.escape()`         |
 | JSON数据 | `\u2028` `\u2029` | `JSON.stringify`          |
  • 对于不明确的类型

    • 最好不要使用输入过滤,因为当把转义后的数据发送到后端再回到前端,赋值给一个变量 之后,展示出来的效果会变成乱码 (如5<7会变成5&lt7

预防存储型XSS和反射型XSS

复制代码
 ##### 纯前端渲染

 * 现代框架的自动防护

 * **React**:默认转义所有插值内容

   ```react
   function UserContent({ text }) {
     return <div>{text}</div>;  // 自动转义 `<` `>` 等字符
   }
   // 用户输入 `<script>alert('XSS攻击')</script>` 会显示为文本,不会执行
   ```

 * **Vue**:模板插值自动编码

   ```vue
   <template>
     <div>{{ userInput }}</div>  <!-- 自动转换为文本 -->
   </template>
   ```

 * **Angular**:插值绑定安全处理

   ```html
   <div>{{ userInput }}</div>  <!-- 输出内容自动转义 -->
   ```

 | 安全API                                    | 危险替代品                | 场景      |
 |:-----------------------------------------|:---------------------|:--------|
 | `textContent`                            | `innerHTML`          | 插入纯文本内容 |
 | `setAttribute`                           | `innerHTML`          | 设置元素属性  |
 | `document.createElement` + `appendChild` | `insertAdjacentHTML` | 动态创建节点  |
复制代码
 ##### 转义HTML

 * 使用更完善更细致的转义库`org.owasp.encoder`,以下代码引用自 [org.owasp.encoder 的官方说明](https://link.juejin.cn/?target=https%3A%2F%2Fwww.owasp.org%2Findex.php%2FOWASP_Java_Encoder_Project%23tab%3DUse_the_Java_Encoder_Project "https://link.juejin.cn/?target=https%3A%2F%2Fwww.owasp.org%2Findex.php%2FOWASP_Java_Encoder_Project%23tab%3DUse_the_Java_Encoder_Project")。

   ```html
   <!-- HTML 标签内文字内容 -->
   <div><%= Encode.forHtml(UNTRUSTED) %></div>

   <!-- HTML 标签属性值 -->
   <input value="<%= Encode.forHtml(UNTRUSTED) %>" />

   <!-- CSS 属性值 -->
   <div style="width:<= Encode.forCssString(UNTRUSTED) %>">

   <!-- CSS URL -->
   <div style="background:<= Encode.forCssUrl(UNTRUSTED) %>">

   <!-- JavaScript 内联代码块 -->
   <script>
     var msg = "<%= Encode.forJavaScript(UNTRUSTED) %>";
     alert(msg);
   </script>

   <!-- JavaScript 内联代码块内嵌 JSON -->
   <script>
   var __INITIAL_STATE__ = JSON.parse('<%= Encoder.forJavaScript(data.to_json) %>');
   </script>

   <!-- HTML 标签内联监听器 -->
   <button
     onclick="alert('<%= Encode.forJavaScript(UNTRUSTED) %>');">
     click me
   </button>

   <!-- URL 参数 -->
   <a href="/search?value=<%= Encode.forUriComponent(UNTRUSTED) %>&order=1#top">

   <!-- URL 路径 -->
   <a href="/page/<%= Encode.forUriComponent(UNTRUSTED) %>">

   <!--
     URL.
     注意:要根据项目情况进行过滤,禁止掉 "javascript:" 链接、非法 scheme 等
   -->
   <a href='<%=
     urlValidator.isValid(UNTRUSTED) ?
       Encode.forHtml(UNTRUSTED) :
       "/404"
   %>'>
     link
   </a>

   ```

预防DOM型XSS

  • 使用.innerHTML.outerHTMLdocument.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent.setAttribute()
  • DOM 中的内联事件监听器,如 locationonclickonerroronloadonmouseover 等,<a> 标签的 href 属性,JavaScript 的 eval()setTimeout()setInterval() 等,都能把字符串作为代码运行

其他预防方法

  • httpOnly: 在 cookie 中设置 HttpOnly 属性后,js脚本将无法读取到 cookie 信息。

    js 复制代码
    // 后端实现
    app.post('/login', (req, res) => {
      res.cookie('sessionID', 'xxxx', {
        httpOnly: true,  // 关键:禁止JS访问
        secure: true,    // 仅通过HTTPS传输
        sameSite: 'Lax'  // 防御CSRF
      });
      res.send('登录成功');
    });
  • 白名单:

    • 输入白名单验证

      js 复制代码
      function validateInput(input) {
          // 只允许字母、数字、空格以及特定的标签
          const whiteListPattern = /^[a-zA-Z0-9\s<>/bB/iI]*$/;
          return whiteListPattern.test(input);
      }
      const userInput = "<script>alert('xss')</script>";
      if (!validateInput(userInput)) {
          // 处理非法输入
          console.log("Invalid input!");
      }
    • HTML标签/属性白名单

      js 复制代码
      // 使用DOMPurify库配置白名单
      import DOMPurify from 'dompurify';
      
      const dirtyHTML = '<b class="safe">合法内容</b><script>恶意代码</script>';
      const cleanHTML = DOMPurify.sanitize(dirtyHTML, {
        ALLOWED_TAGS: ['b', 'i', 'em'],     // 允许的标签
        ALLOWED_ATTR: ['class', 'style'],   // 允许的属性
        FORBID_TAGS: ['style', 'script']    // 强制禁止的标签
      });
      
      console.log(cleanHTML); 
      // 输出:<b class="safe">合法内容</b>
  • CSP:

    • 基础配置

      http 复制代码
      Content-Security-Policy:
        default-src 'self';     # 默认只允许同源资源
        script-src 'self'       # 脚本仅限同源
          https://trusted.cdn.com 
          'nonce-abc123';       # 允许带特定nonce的内联脚本
        style-src 'self' 'unsafe-inline'; # 允许内联样式(慎用)
        img-src * data:;        # 允许所有图片源(根据需求收紧)
        font-src 'self'; 
        frame-src 'none';       # 禁止嵌入iframe
        report-uri /csp-report; # 违规报告地址

参考资料 juejin.cn/post/684490... juejin.cn/post/684490...

相关推荐
小吕学编程8 分钟前
ES练习册
java·前端·elasticsearch
Asthenia041215 分钟前
Netty编解码器详解与实战
前端
Blossom.11818 分钟前
量子网络:构建未来通信的超高速“高速公路”
网络·opencv·算法·安全·机器学习·密码学·量子计算
袁煦丞20 分钟前
每天省2小时!这个网盘神器让我告别云存储混乱(附内网穿透神操作)
前端·程序员·远程工作
落——枫34 分钟前
网络安全知识点3
安全·web安全
柴郡猫^O^1 小时前
OSCP - Proving Grounds - Wpwn
安全·网络安全·安全性测试
安全系统学习1 小时前
网络安全之红队LLM的大模型自动化越狱
运维·人工智能·安全·web安全·机器学习·php
一个专注写代码的程序媛1 小时前
vue组件间通信
前端·javascript·vue.js
一笑code1 小时前
美团社招一面
前端·javascript·vue.js
懒懒是个程序员2 小时前
layui时间范围
前端·javascript·layui