5.1 XSS:寄生在页面里的脚本


5.1 XSS:寄生在页面里的脚本

本节摘要:本节拆解跨站脚本攻击:脚本如何借用户内容进入页面并寄生执行,反射型、存储型、DOM 型的入口差异,脚本到手后能做什么(偷会话、改页面、代发请求);蓝队侧按上下文输出编码配 CSP 的组合防线。XSS 与注入同宗——都是数据越界成指令,只是这次执行的场地在浏览器。

寄生在页面里的代码

前端回合开打,红队瞄准的第一个目标是别的用户的浏览器。浏览器有个根深蒂固的假设:页面上执行的脚本都代表这个站点的意志。同源策略之下,脚本能读这个站点的 Cookie、能改这个站点的 DOM、能以用户身份向这个站点发请求——这套能力本来是给站点自己用的。XSS 做的事,就是把攻击者的脚本混进页面,让它继承这套全套权限

与 SQL 注入对照着理解最省力:注入是用户输入越界成数据库指令,XSS 是用户输入越界成浏览器指令。成因同源(输出时未处理),防御同思路(输出按上下文处理),只是"指令解释器"从数据库换成了浏览器。

三种寄生方式

反射型:输入随请求"弹"回页面——搜索结果页回显关键词、报错页回显参数,攻击者把带毒链接发给受害者点开。存储型:输入被存进数据库,此后每个浏览到该位置的用户都中招——评论区、个人简介、工单标题都是经典宿主,一枚存储型 XSS 足以静候管理员上钩。DOM 型:数据不经过服务器,纯在前端流转——页面脚本把地址栏参数、消息事件里的数据未经处理写进文档,服务端日志里甚至看不见痕迹。三型的差异决定检测方式:存储型有"入库—出库"两处可查,反射型只在流量里,DOM 型连流量都可能干干净净。

三种寄生方式

靶场演示:一条评论的旅行

存储型 XSS 的靶场复现(反面教材 + 最小化演示载荷):

# 反面教材:评论入库与渲染都不处理(靶场复现) def post_comment(user, content: str): db.insert("comments", user_id=user.id, content=content) # 原样入库 def render_comments(): rows = db.query("SELECT content FROM comments ORDER BY id DESC") return "".join(f"<div class='c'>{r['content']}</div>" for r in rows) # 原样拼进 HTML——content 里的标签会被浏览器当真执行

攻击者提交的"评论"(最小化演示载荷,仅证明执行):

<b>尺码偏小,建议拍大一码</b><img src=x onerror="alert(document.domain)"> <!-- 渲染后 img 加载失败触发内联脚本;真实攻击里这里换成外带会话的请求 -->

此后每个浏览评论页的用户,页面上都安静地跑着攻击者的代码。演示止步于弹窗证明——把 alert 换成外带逻辑的完整载荷属于武器化范畴,不在此册展开。

输出编码与 CSP

对症的修法是按上下文编码——数据落在哪种语法位置,就按哪种规则转义:

# 整改版:按上下文的输出编码 from markupsafe import escape def render_comments(): rows = db.query("SELECT content FROM comments ORDER BY id DESC") # 元素上下文:标签与引号全部转义,浏览器只见文本不见标签 return "".join(f"<div class='c'>{escape(r['content'])}</div>" for r in rows) # 若必须允许部分富文本:白名单净化而非黑名单过滤 # 允许 b/i/p 与 href 限 http(s),其余标签属性一律剥除(成熟净化库语义)

要点在"上下文"二字:同样的数据写进元素、属性、脚本字符串、URL,编码规则各不相同——属性里要管引号闭合,脚本里要管反斜杠与引号,URL 里要管协议头。一刀切的"全局转义"是常见败笔。DOM 型的对应修法是前端弃用直接写 HTML 的接口,改用创建文本节点的安全接口,并禁止把不可信数据写进脚本上下文。

CSP 作为纵深第二层,给脚本执行再上一道锁:

# 内容安全策略:只许同源脚本与指定样式,禁内联(上线前用报告模式观察) Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'

即便编码疏漏放进来一段脚本,内联与外源均被拒绝,寄生失败。注意 CSP 不是编码的替代——它是纵深,不是免罪牌。

检测与应急

检测分两路:流量侧找带脚本特征的参数(标签、事件属性、协议头组合);行为侧找"页面发出了用户没发起的请求"。CSP 报告接口是免费的检测器,违规报告聚积的位置就是编码欠账的位置。应急上,发现存储型 XSS 立即下线受染内容并排查同作者历史提交,评估寄生期间浏览过该页面的用户量,必要时全员换发会话令牌(呼应会话回合的"吊销优先"原则)。

自查一个动作就够:在自家评论区输入一组标签再刷新。若它原样变成了页面元素,这一节就是为你写的。

富文本场景的特别说明

评论区完全禁止标签当然最安全,但产品总要"加粗、引用、贴链接"。富文本场景的工程解法是净化而非过滤:用成熟的净化库按白名单解析与重建文档树,允许的标签与属性之外的统统剥掉。三个高频翻车点提前打好招呼。其一,链接属性的协议校验——允许了 a 标签就要限定协议头,否则"某链接协议"能执行脚本,这是净化库历史上最著名的坑。其二,嵌套构造——剥一层标签后拼接出合法标签的绕法,白名单重建(而不是黑名单删除)天然免疫。其三,净化与编码的顺序——先净化入库、出库仍要按上下文编码,两道工序不能互相顶替。把这三点抄给实现富文本的同事,能省掉一轮安全评审返工。最后一个心态提醒:富文本是 XSS 防线上的永久工地,净化库的更新要跟进(历史上的绕过大多由库的新版本修复),把净化库版本纳入依赖审计清单,别让"当年配好的"悄悄过期。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U