CSP 绕过与 XSS 攻击详解:原理、方法与防御
2024-12-19 14:49:06
内容安全策略(CSP)绕过与跨站脚本攻击(XSS)实施
实施内容安全策略(CSP)是提升Web应用安全的重要手段。通过设定规则, 能限制浏览器加载和执行特定来源的资源, 有效地减少XSS等攻击的风险。然而, 即便存在严格的CSP规则, 攻击者依然有可能尝试通过多种技术手段来绕过防护, 实施XSS攻击。
问题剖析: CSP 限制下的 XSS 攻击可能性
应用场景:假设一个Web应用程序,设置了CSP来防范XSS攻击,并采用了Helmet中间件。设置了以下CSP规则:
.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"use.fontawesome.com",
"ajax.googleapis.com",
"cdnjs.cloudflare.com",
],
},
})
)
这条策略指定了默认来源是同源。 也允许从use.fontawesome.com
, ajax.googleapis.com
, 和 cdnjs.cloudflare.com
加载脚本。
在这样的设置下,传统的<script>alert(1)</script>
这种简单脚本注入会被阻止。用户如果输入了HTML代码,代码本身会被显示出来。可是,即使可以提交JavaScript代码,由于CSP限制,并不能执行。尽管如此, 在允许用户输入HTML内容, 且缺乏合适的过滤或编码机制情况下, 攻击者仍可能寻找机会来绕过限制,执行恶意脚本。
绕过CSP与实施XSS的途径
CSP机制存在弱点时, 攻击者有多种策略可以绕过这些限制,并执行XSS攻击。以下几种方法较为常见:
1. 利用白名单中的第三方服务
CSP中 script-src
指令常常会引入第三方脚本服务, 如 ajax.googleapis.com
、 cdnjs.cloudflare.com
。
攻击者可以尝试在这些被允许的服务中, 寻找已知的具有执行脚本能力或者存在漏洞的版本, 尝试发起XSS攻击。
譬如,如果网站允许了某个CDN的域名,而该CDN上恰好托管了一个存在漏洞的旧版本JavaScript库(例如存在XSS漏洞的AngularJS版本), 攻击者便可利用此漏洞。
操作步骤:
- 确认应用程序使用的任何白名单内第三方JavaScript库的具体版本。
- 研究这些库是否存在已知漏洞。
- 构造恶意payload, 例如通过链接加载含有漏洞的版本。
如https://ajax.googleapis.com/ajax/libs/angularjs/1.1.1/angular.min.js
。 这个版本存在CSP绕过漏洞。
代码示例:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.1/angular.min.js"></script>
<div ng-app ng-csp>{{$on.constructor('alert("XSS")')()}}</div>
这个例子中使用了angular,如果目标站点引入并错误地使用了老版本Angular, 通过 AngularJS 的特性执行了任意代码 alert("XSS")
。
2. 利用HTML标签的特性执行脚本
即便是严格的CSP,在不恰当处理HTML输入时,仍存在绕过的可能性。
如果某些可能执行脚本的HTML标签和事件属性(如 onerror
、onload
等)没有被充分限制或过滤,就存在利用可能。
操作步骤:
- 尝试提交多种包含内联事件处理器的HTML标签。
- 通过反复试验确定哪些标签和属性可以成功执行。
代码示例:
<img src='x' onerror='alert(1)'>
即使 script-src
设置限制了外部脚本加载, <img src='x' onerror='alert(1)'>
依旧尝试执行alert(1)
, 如果服务器对 <img>
标签的事件处理器过滤不严, 此攻击可能成功。
3. 寻找JSONP端点的漏洞
应用程序使用的第三方API如果是通过JSONP方式进行通信的, JSONP回调函数可能会成为一个弱点。
操作步骤:
- 审查所有使用JSONP方式进行通信的外部接口。
- 尝试修改回调函数来执行攻击载荷。
代码示例:
假定网站上使用了如下JSONP URL:
https://example.com/api/data?callback=parseResponse
修改请求, 让回调函数成为攻击代码:
https://example.com/api/data?callback=alert(1);
这个请求如果服务器没有充分验证 callback
参数, 可能直接拼接 callback
的值到响应中, 从而导致脚本执行。
4. 内部脚本执行
即便设置了正确的 script-src
指令, 应用程序内部自身的脚本逻辑缺陷,也有可能执行不该执行的逻辑, 触发 XSS 漏洞。比如, 如果对用户输入的数据处理不当,再进行页面输出, 也会有被利用的风险。
代码示例:
设想页面中存在以下 JavaScript 代码段:
let userInput = getUserInput(); // 从用户获取的输入, 未正确过滤或转义
let div = document.createElement('div');
div.innerHTML = "<img src=x onerror=alert(userInput)>"; // 这里尝试根据用户输入渲染 HTML
document.body.appendChild(div);
尽管设置了 CSP, 这段代码自身创建了一个可导致 XSS 的环境。
安全建议及最佳实践
1. 输入数据净化和转义: 对所有用户输入数据, 都要进行正确的编码或转义,再进行输出,这才能确保用户提供的数据不会被当做代码执行。
2. 严格的白名单: 定期审查和更新CSP中的白名单列表, 减少可能的风险源, 避免不必要的外部资源引入。使用可信的CDN, 且限制具体的脚本版本, 如通过子资源完整性(SRI)机制来加强。
代码示例(SRI):
<script src="https://use.fontawesome.com/releases/v5.15.3/js/all.js" integrity="sha384-XXX" crossorigin="anonymous"></script>
使用 integrity
属性确保只有预期版本的脚本能被加载。
3. 对动态内容添加过滤: 当应用场景不允许采用彻底的输入过滤,需要保持一部分HTML格式输出时, 使用可信的库来清除 HTML 中不安全的部分,如 DOMPurify。
代码示例:
import DOMPurify from 'dompurify';
let userInput = getUserInput(); // 获取用户输入
let clean = DOMPurify.sanitize(userInput); // 清理用户输入, 只留下安全的内容
// ... 使用 clean 变量进行页面输出 ...
4. 避免使用内联事件处理: 遵循最佳实践,将 JavaScript 代码从 HTML 结构中分离,避免使用内联事件处理程序。
5. JSONP的安全应用: 严格校验 JSONP 回调函数名称,避免随意执行第三方服务传递过来的参数值。