XSS攻击你是否真的了解?案例细说,XSS供方两端的较量

首先问自己几个问题:知道什么是XSS吗?XSS有哪几类?如果你是一个网站攻击者,你会用XSS怎么攻击网站?XSS又如何预防?

如果你能清晰地回忆起所有概念和技术细节,那么恭喜你可以退出了。如果你还有所疑问请接着往下看。我会出一些例子来具体说明。最详细的综合案列在最后面说!

什么是XSS

XSS (Cross-Site Scripting) 是一种攻击类型,攻击者将恶意的脚本代码注入到其他用户信任的网页中。当受害者访问这个被注入了恶意脚本的网页时,脚本会在受害者的浏览器中执行。因为脚本是在目标网站的上下文中执行的,所以它可以所很多事情。比如:窃取用户的会话 Cookie、Token 等凭证,导致攻击者能完全控制用户账户。

那么XSS攻击有几类呢?

存储型 XSS

存储型 XSS 的攻击步骤:

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

这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。这种比较容易理解:我在论坛网站上发布一个

反射型 XSS

反射型 XSS 的攻击步骤:

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

反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。

由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。

DOM 型 XSS:

与前两种不同,DOM 型 XSS 的漏洞完全存在于客户端的 JavaScript 代码中,不涉及服务器。

恶意脚本的注入和执行是由于前端 JavaScript 代码(如 innerHTML, document.write, eval, location.href, setTimeout 等)不安全地处理了用户可控的数据(如 URL 片段、表单输入)导致的。

攻击流程通常也是通过诱使用户点击恶意链接。

关键点: 恶意负载在数据到达服务器之前就被浏览器端的 JavaScript 解析并执行了。

注意:反射型XSS和DOM型XSS的共同点都是通过诱导用户点击某个链接将url的特定参数解析后在目标网站上执行获取用户信息。不同点在于,DOM型XSS的url上特定参数是不会传到服务的,是客户端(前端)自身的问题。常见的是通过url传递参数,然后用来获取默认值或者传递某个用户可控制的参数用来显示在页面上

一个案例解释DOM型XSS:

xml 复制代码
<!DOCTYPE html>
<html>
<body>
  <input type="text" id="expression" readonly>
  <div id="result"></div>

  <script>
    // 1. 从 URL 片段 (# 后面的内容) 获取表达式
    var userInput = decodeURIComponent(window.location.hash.substring(1)); // 例如:访问 #1+1 会得到 "1+1"

    // 2. 将表达式显示在输入框 (这里使用了不安全的 innerHTML)
    document.getElementById('expression').innerHTML = "计算: " + userInput;

    // 3. 尝试计算并显示结果 (这里使用了极其危险的 eval)
    try {
      var calcResult = eval(userInput); // ⚠️⚠️⚠️ 极度危险的操作!直接 eval 用户输入!
      document.getElementById('result').innerHTML = "结果: " + calcResult;
    } catch (e) {
      document.getElementById('result').innerHTML = "错误: 无效表达式";
    }
  </script>
</body>
</html>

在这个案例中使用了eval来执行Js计算值导致可以直接注入JS脚本:

less 复制代码
// 假设上面代码的请求地址是这样的,参数还是通过描点
https://example.com/calculator.html#';alert('XSS');//

这样就可以直接注入攻击,就算你不使用eval函数,直接使用innnerHtml,我也可以:

xml 复制代码
https://example.com/calculator.html#<img src=x onerror=alert('XSS')>1

直接注入标签的形式让onerror回调立即执行...所以慎重选择innerHtml,在这个案例中完全可以不使用它,这里只是举例说明。

综合案例

一个案例带你感受XSS攻防两端的魅力。

一天,在一个电商公司的前端开发Q仔根据公司业务需求开发了一个页面。根据url参数决定关键词的内容:

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

几天后,Q仔收到安全团队发来的一个神秘链接,内容为:

xml 复制代码
http://xxx/search?keyword="><script>alert('XSS');</script>

Q仔觉得安全团队找上门。肯定不是好事,心里忐忑的点开了...

明晃晃的alert带着XSS的字样出现在了Q仔眼前。完了,有漏洞。Q仔没慌,研究后发现: 当浏览器请求 http://xxx/search?keyword=">&lt;script>alert('XSS');&lt;/script> 时,服务端会解析出请求参数 keyword,得到 "><script>alert('XSS');</script>,拼接到 HTML 中返回给浏览器。形成了如下的 HTML:

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

input标签被闭合,紧跟着的事一个script标签,带着它的恶意代码就执行了。同样div标签也被闭合了,这段恶意代码还会执行两次。

Q仔开发多年,这点事儿难不倒他,利用escapeHTML:

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

转义后:

xml 复制代码
<input type="text" value="&quot;&gt;&lt;script&gt;alert(&#x27;XSS&#x27;);&lt;&#x2F;script&gt;">
<button>搜索</button>
<div>
  您搜索的关键词是:&quot;&gt;&lt;script&gt;alert(&#x27;XSS&#x27;);&lt;&#x2F;script&gt;
</div>

只不过浏览器会转义回来按照正常字符来显示,问题完美搞定了。Q仔还在其他需要转义的地方都做了处理,开始躺平。

不久后,安全团队又发来一个链接:[http://xxx/?redirect_to=javascript:alert('XSS](https://link.juejin.cn?target=http%3A%2F%2Fxxx%2F%3Fredirect_to%3Djavascript%3Aalert('XSS "http://xxx/?redirect_to=javascript:alert('XSS")')

这个页面里有这样的代码:

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

反射执行后就变成了:

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

不会立即执行,但是点击就完了...

Q仔心想:那就不让它以javascript开头就好了:

ini 复制代码
// 禁止 URL 以 "javascript:" 开头
xss = getParameter("redirect_to").startsWith('javascript:');
if (!xss) {
  <a href="<%= escapeHTML(getParameter("redirect_to"))%>">
    跳转...
  </a>
} else {
  <a href="/404">
    跳转...
  </a>
}

安全团队再给一个:[http://xxx/?redirect_to=jAvascRipt:alert('XSS](https://link.juejin.cn?target=http%3A%2F%2Fxxx%2F%3Fredirect_to%3DjAvascRipt%3Aalert('XSS "http://xxx/?redirect_to=jAvascRipt:alert('XSS")')

Q仔裂开来,马上处理全部转成小写来判断,这下你没招了吧。

安全又来一个:[http://xxx/?redirect_to= javascript:alert('XSS](https://link.juejin.cn?target=http%3A%2F%2Fxxx%2F%3Fredirect_to%3D%2520javascript%3Aalert('XSS "http://xxx/?redirect_to= javascript:alert('XSS")')

%20javascript:alert('XSS') 经过 URL 解析后变成 javascript:alert('XSS'),这个字符串以空格开头。这样攻击者可以绕过后端的关键词规则,又成功的完成了注入。

Q仔内心:我现在辞职还来得及吗?这是被针对了啊!不过后来冷静之后想到了一个办法:

加入白名单:http,https。

ini 复制代码
// 根据项目情况进行过滤,禁止掉 "javascript:" 链接、非法 scheme 等
allowSchemes = ["http", "https"];

valid = isValid(getParameter("redirect_to"), allowSchemes);

if (valid) {
  <a href="<%= escapeHTML(getParameter("redirect_to"))%>">
    跳转...
  </a>
} else {
  <a href="/404">
    跳转...
  </a>
}

问题彻底解决了,Q仔也经受住了考验。

深层启示:XSS防御需覆盖输入、输出、传输、执行四层链条,且须持续迭代。"XSS是认知战,开发者需理解数据流动的每一处缝隙,而攻击者永远在寻找下一扇窗。

相关推荐
崔庆才丨静觅16 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606117 小时前
完成前端时间处理的另一块版图
前端·github·web components
Hello.Reader17 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
掘了17 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅17 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅17 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
智驱力人工智能17 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
崔庆才丨静觅18 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment18 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
数据与后端架构提升之路18 小时前
论系统安全架构设计及其应用(基于AI大模型项目)
人工智能·安全·系统安全