第二章:XSS攻击原理——反射型、存储型、DOM型XSS的完整攻击链路

大家好,我是你们的前端安全讲师。今天我们来聊聊XSS攻击。

说实话,XSS(跨站脚本攻击)是我在项目中遇到最多的安全问题。没有之一。我记得刚入行那会儿,总觉得XSS离自己很远,直到有一次线上系统被注入了恶意脚本,用户数据差点泄露……嗯,从那以后,我再也不敢轻视它了。

XSS说白了就是攻击者把恶意脚本塞到你的网页里,然后让浏览器执行。为什么会这样?因为浏览器太“信任”用户输入了。你想想看,它分不清哪些是正常代码,哪些是恶意代码。

XSS主要分三种:反射型、存储型、DOM型。咱们一个一个来看。

一、反射型XSS(非持久型)

反射型XSS,也叫非持久型XSS。它的特点是:恶意脚本不会存到服务器上,而是通过URL参数“反射”回来。

攻击链路是这样的:

  1. 攻击者构造一个恶意链接,比如:https://example.com/search?q=<script>alert('xss')</script>
  2. 用户点击了这个链接
  3. 服务器收到请求,把参数q的值直接拼到了HTML里返回
  4. 浏览器解析HTML,执行了<script>alert('xss')</script>
  5. 攻击成功

关键点:反射型XSS需要用户主动点击恶意链接。攻击者通常会通过钓鱼邮件、短信、社交平台来传播这个链接。

我在项目中遇到过这样一个案例:一个搜索框,用户输入关键词后,页面会显示“您搜索的是:xxx”。后端直接把关键词拼到了HTML里,没有任何过滤。结果呢?攻击者构造了一个链接,里面带了恶意脚本,用户一点击,cookie就被偷走了。

避坑指南:我曾经以为只要对<script>标签做过滤就够了。后来发现攻击者可以用<img onerror><svg onload>等方式绕过。所以,永远不要信任用户输入

二、存储型XSS(持久型)

存储型XSS,也叫持久型XSS。这是最危险的一种。为什么?因为恶意脚本被存到了服务器上,所有访问这个页面的用户都会中招。

攻击链路是这样的:

  1. 攻击者在评论区、留言板、个人简介等地方提交恶意代码
  2. 服务器没有做过滤,直接把恶意代码存到了数据库里
  3. 其他用户访问这个页面时,服务器从数据库取出恶意代码,拼到HTML里返回
  4. 浏览器执行恶意脚本
  5. 所有访问者都成了受害者

注意:存储型XSS的危害范围最大。一个漏洞可能影响成千上万的用户。我见过一个论坛因为存储型XSS,导致管理员账号被盗,整个站点被篡改。

我记得有个经典案例:某社交平台允许用户在个人签名里写HTML代码。攻击者写了一段脚本,自动关注自己的账号。结果呢?所有看过他个人主页的用户,都莫名其妙关注了他。这就是存储型XSS的威力。

存储型XSS和反射型XSS的区别在于:

对比项 反射型XSS 存储型XSS
恶意代码存储位置 URL中 服务器数据库
触发方式 用户点击恶意链接 用户访问正常页面
危害范围 单个用户 所有访问者
检测难度 较低 较高

三、DOM型XSS

DOM型XSS跟前两种不太一样。它不经过服务器,完全在客户端发生。说白了,就是JavaScript操作DOM时,把恶意数据当成了代码执行。

攻击链路是这样的:

  1. 攻击者构造恶意链接,比如URL的hash或search参数里带了恶意代码
  2. 用户点击链接,页面加载
  3. 前端JavaScript代码从URL中取出参数,直接通过innerHTMLdocument.write等方式插入到DOM中
  4. 浏览器执行了恶意脚本
  5. 攻击成功

关键点:DOM型XSS的恶意代码不经过服务器。服务器返回的HTML是正常的,但客户端的JS代码“创造”了漏洞。

我举个例子:

// 危险代码
var name = new URLSearchParams(window.location.search).get('name');
document.getElementById('welcome').innerHTML = '欢迎您,' + name;

// 如果URL是:https://example.com?name=<img src=x onerror=alert(1)>
// 那么恶意代码就会被执行

为什么会这样?因为innerHTML会把字符串当作HTML解析。攻击者只要在参数里塞一个<img onerror>或者<svg onload>,就能执行任意脚本。

避坑指南:我曾经在项目里用innerHTML渲染用户评论,觉得只要后端过滤了就行。后来发现DOM型XSS根本不需要后端参与。所以,前端也要做过滤。用textContent代替innerHTML,或者用DOMPurify库做净化。

三种XSS的对比总结

类型 存储位置 触发方式 是否需要服务器参与 危害等级
反射型 URL 点击恶意链接
存储型 数据库 访问正常页面
DOM型 URL/本地存储 点击恶意链接 中高

嗯,到这里三种XSS的攻击链路就讲完了。你可能会问:怎么防御?别急,后面几章我会专门讲防御方案。但今天你得先记住一个核心原则:永远不要信任用户输入,永远不要信任URL参数,永远不要用innerHTML直接插入用户数据

我个人习惯在写代码时,只要遇到innerHTMLdocument.writeevalsetTimeout字符串参数这些API,就会停下来想一想:这里的数据来源可靠吗?会不会被攻击者控制?

好了,这一章就到这里。下一章我们聊聊XSS的实战防御方案,包括CSP、输入过滤、输出编码这些干货。到时候见。