flawopen.com/Swift 中的 XSS/loadHTMLString
iOS 的 WKWebView.loadHTMLString 能安全处理不可信内容吗?
不能自动保证安全:WKWebView 会将传入字符串解析为 HTML,因此标签和可执行脚本的标记仍然有效,除非内容可信或已按正确上下文处理。普通文本与用户提供的富 HTML 需要不同的控制措施。
安全用法
let html = "\(untrustedText)
" webView.loadHTMLString(html, baseURL: nil)
func escapeHTMLText(_ value: String) -> String {
value
.replacingOccurrences(of: "&", with: "&")
.replacingOccurrences(of: "<", with: "<")
.replacingOccurrences(of: ">", with: ">")
.replacingOccurrences(of: "\"", with: """)
.replacingOccurrences(of: "'", with: "'")
}
let html = "\(escapeHTMLText(untrustedText))
"
webView.loadHTMLString(html, baseURL: nil)
需要检查的额外风险
页面世界中的消息处理器为页面脚本提供了调用应用代码的通道。`WKContentWorld` 可以隔离 JavaScript 环境,但页面脚本和应用脚本仍共享 DOM。如果不可信 HTML 与桥接接口并存,只暴露最小能力,验证消息及其 frame,并单独授权每项操作;不要把环境隔离当成授权。
baseURL 控制什么
Apple 将 `baseURL` 定义为解析所提供 HTML 中相对 URL 的基准。它不会净化标记,也不会关闭页面 JavaScript。设为 `nil` 不能防止 XSS,也不代表启用了沙箱;资源和导航策略需要另行设置。
针对每个边界选择控制措施
普通文本嵌入 HTML 文本节点时,使用专用于该上下文的编码器,例如下方代码;不要将它复用于属性、URL、CSS 或 JavaScript。用户提供富 HTML 时,使用持续维护的净化器,并明确允许的元素、属性和 URL scheme。通过 `callAsyncJavaScript` 参数向应用脚本传值,不要把值拼接进源码。`WKContentWorld` 会隔离 JavaScript 环境,但 DOM 仍共享,因此它不会授权原生操作。将桥接接口限制到最小,并验证每条消息和操作。如果页面不需要 JavaScript,`allowsContentJavaScript` 可以作为纵深防御关闭页面脚本;它不能取代正确的输出处理。
常见问题
没有:它有相同风险,也没有额外的内置净化机制。迁移到 WKWebView 是技术更新,本身并不能修复安全问题。
不能。Apple 将 `baseURL` 用于解析相对 URL。它不是 HTML 净化器、脚本开关或沙箱保证。
不会。它隔离 JavaScript 环境,但这些环境共享 DOM。应将 DOM 数据和消息视为不可信,并单独授权每项原生操作。
参考资料
- Apple Developer — WKWebView
- flawopen.com — Swift 中的 XSS
- Apple — WKWebView.callAsyncJavaScript(_:arguments:in:in:completionHandler:)
- Apple — WKContentWorld
- Apple — WKUserContentController.add(_:contentWorld:name:)
- Apple — WKWebpagePreferences.allowsContentJavaScript
- OWASP — 跨站脚本防护指南
- Apple — WKWebView.loadHTMLString(_:baseURL:)