flawopen.com/跨站脚本攻击
跨站脚本攻击
当页面以浏览器会解析为 HTML 或 JavaScript 的方式嵌入某个用户的文本时,这段文本就会以网站自身的权限在其他用户那里运行,这就是跨站脚本攻击。修复方法在任何地方都一样:按值写入的确切位置进行编码,对少数必须允许 HTML 的字段做净化,并用 Content Security Policy 为两者兜底。在下方选择你的语言,查看确切的 API、漏洞代码与修复代码,以及能在 CI 中发现问题的规则。
通俗易懂的原理解析 (ELI5)
报纸会在自己的版面上刊登读者来信。有位读者寄来一封排版得和编辑部公告一模一样的信:“订户请拨打此号码并报出银行卡信息以续订。”因为它用报纸自己的字体印了出来,读者就像相信编辑部亲笔一样相信它。XSS 就是网站把访客的文字印进自己的页面,浏览器便把它当作网站自己的代码来执行,并拥有网站对你账户的访问权。转义则是把每封来信都印在醒目的引文框里,让任何来信都无法冒充编辑部公告。
核心概念与专有名词
- Cross-site scripting (CWE-79)
- 网页嵌入了不可信数据,却没有针对浏览器对其进行无害化处理,于是其中的标记或脚本在页面的源中被解析和执行,可以访问页面的 DOM、cookie 和 API。
- Reflected, stored and DOM-based XSS
- 反射型 XSS 把请求数据直接回显在响应中,通常借助精心构造的链接。存储型 XSS 把值保存下来,展示给每一位查看者。基于 DOM 的 XSS 根本不经过服务器:客户端 JavaScript 把 URL 或存储中的数据复制到
innerHTML之类的接收点。 - Contextual output encoding
- 每种输出上下文都需要各自的编码:元素文本和带引号的属性用 HTML 实体,脚本内部用 JavaScript 字符串转义,URL 参数用百分号编码,
href和src还要检查协议。放进脚本里的值只做 HTML 编码是不够的。 - Sanitizing vs encoding
- 编码会把标记显示成文本。当用户必须提交格式化内容时,要让 HTML 经过白名单净化器(DOMPurify、bleach 的继任者 nh3、OWASP Java HTML Sanitizer、HtmlSanitizer、ammonia),保留允许的标签,删除脚本、事件处理器和
javascript:URL。 - Content Security Policy (CSP)
- 一个告诉浏览器哪些脚本可以运行的响应头。基于 nonce、且不含
'unsafe-inline'的script-src,即使编码出了漏洞也能拦下大多数注入脚本;require-trusted-types-for 'script'则把 DOM 接收点锁住。
攻击执行流程分解
不可信文本进入页面
搜索词、个人资料字段、评论、URL 片段或 API 响应,在服务端构建页面或浏览器更新页面时被嵌入。
写入时没有使用正确的编码
模板转义被关闭,值进入了 HTML 转义覆盖不到的脚本或 URL 上下文,或者客户端代码把它赋给了 innerHTML。无害的测试值 <img src=x onerror=alert(1)> 完好无损地保留下来。
浏览器把它当作代码解析
受害者的浏览器构建出真实的 <img> 元素并触发 onerror,或者在点击时执行 javascript: 链接。
脚本以网站的身份行事
它以网站的源运行:可以读取页面,带着受害者的 cookie 或令牌调用网站 API,篡改用户看到的内容,并以用户的名义提交表单。
各编程语言的 XSS 指南
工程与系统安全加固清单
- 所有页面都用开启自动转义的模板引擎渲染,并审查每一处例外(
|safe、Html.Raw、dangerouslySetInnerHTML、raw、unsafe { }、{!! !!})。 - 按确切的上下文编码:HTML 文本和带引号的属性、JavaScript 字符串、URL 参数;绝不通过字符串拼接构造 HTML 或 JavaScript。
- 用户提供的 HTML 在渲染处用持续维护的白名单净化器净化。
- 用户提供的 URL 在进入
href、src或页面跳转之前,只允许http和https。 - 发送基于 nonce、不含
'unsafe-inline'的 Content-Security-Policy,并附带X-Content-Type-Options: nosniff。 - 会话令牌放在
HttpOnly、Secure、SameSite=Lax的 cookie 中,而不是localStorage。