FlawOpen

flawopen.com/Swift 中的 XSS/loadHTMLString

iOS 的 WKWebView.loadHTMLString 能安全处理不可信内容吗?

参考页面
简答

不能自动保证安全:WKWebView 会将传入字符串解析为 HTML,因此标签和可执行脚本的标记仍然有效,除非内容可信或已按正确上下文处理。普通文本与用户提供的富 HTML 需要不同的控制措施。

安全用法

RISKY
let html = "

\(untrustedText)

" webView.loadHTMLString(html, baseURL: nil)
SAFER
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` 可以作为纵深防御关闭页面脚本;它不能取代正确的输出处理。

常见问题

已弃用的 UIWebView 有什么不同吗?

没有:它有相同风险,也没有额外的内置净化机制。迁移到 WKWebView 是技术更新,本身并不能修复安全问题。

`baseURL: nil` 能防止 XSS 吗?

不能。Apple 将 `baseURL` 用于解析相对 URL。它不是 HTML 净化器、脚本开关或沙箱保证。

`WKContentWorld` 会隔离页面 DOM 吗?

不会。它隔离 JavaScript 环境,但这些环境共享 DOM。应将 DOM 数据和消息视为不可信,并单独授权每项原生操作。

参考资料

另请参阅 XSS 跨站脚本攻击(CWE-79):原理与各编程语言的防护方法 — flawopen.com 反射型、存储型与 DOM 型 XSS 有何区别? Android addJavascriptInterface 的安全风险 Angular XSS 防护:安全默认行为与绕过 API MIME 嗅探 XSS:Content-Type 与 nosniff CSP 与输出转义:两者都需要吗? React 的 dangerouslySetInnerHTML 安全吗?