# iOS WebViews ## WebViews types WebViews are in-app browser components for displaying interactive **web** **content**. They can be used to embed web content directly into an app's user interface. iOS WebViews **support** **JavaScript** execution **by default**, so script injection and Cross-Site Scripting attacks can affect them. * \*\*\*\*[**UIWebView**](https://developer.apple.com/documentation/uikit/uiwebview)**:** UIWebView is deprecated starting on iOS 12 and should not be used. It shouldn't be used. JavaScript cannot be disabled. * \*\*\*\*[**WKWebView**](https://developer.apple.com/documentation/webkit/wkwebview): This is the appropriate choice for extending app functionality, controlling displayed content. * **JavaScript** is enabled by default but thanks to the **`javaScriptEnabled`** property of `WKWebView`, it **can be completely disabled**, preventing all script injection flaws. * The **`JavaScriptCanOpenWindowsAutomatically`** can be used to **prevent** JavaScript from **opening new windows**, such as pop-ups. * The **`hasOnlySecureContent`** property can be used to verify resources loaded by the WebView are retrieved through encrypted connections. * `WKWebView` implements out-of-process rendering, so **memory corruption bugs won't affect** the main app process. * \*\*\*\*[**SFSafariViewController**](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller)**:** It ****should be used to provide a **generalized web viewing experience**. These WebViews can be easily spotted as they have a characteristic layout which includes the following elements: * A read-only address field with a security indicator. * An Action \("**Share**"\) **button**. * A **Done button**, back and forward navigation buttons, and a "Safari" button to open the page directly in Safari. ![](https://gblobscdn.gitbook.com/assets%2F-LH00RC4WVf3-6Ou4e0l%2F-Lf1APQHyCHdAvoJSvc_%2F-Lf1AQxr7FPsOyPFSGcs%2Fsfsafariviewcontroller.png?alt=media) * **JavaScript cannot be disabled** in `SFSafariViewController` and this is one of the reasons why the usage of `WKWebView` is recommended when the goal is extending the app's user interface. * `SFSafariViewController` also **shares cookies** and other website data with **Safari**. * The user's activity and interaction with a `SFSafariViewController` are **not visible to the app**, which cannot access AutoFill data, browsing history, or website data. * According to the App Store Review Guidelines, `SFSafariViewController`s **may not be hidden or obscured by other views or layers**. ## Discovering WebViews Configuration ### Static Analysis **UIWebView** ```bash $ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$" 489 0x0002fee9 0x10002fee9 9 10 (5.__TEXT.__cstring) ascii UIWebView 896 0x0003c813 0x0003c813 24 25 () ascii @_OBJC_CLASS_$_UIWebView 1754 0x00059599 0x00059599 23 24 () ascii _OBJC_CLASS_$_UIWebView ``` **WKWebView** ```bash $ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$" 490 0x0002fef3 0x10002fef3 9 10 (5.__TEXT.__cstring) ascii WKWebView 625 0x00031670 0x100031670 17 18 (5.__TEXT.__cstring) ascii unwindToWKWebView 904 0x0003c960 0x0003c960 24 25 () ascii @_OBJC_CLASS_$_WKWebView 1757 0x000595e4 0x000595e4 23 24 () ascii _OBJC_CLASS_$_WKWebView ``` Alternatively you can also search for known methods of these WebView classes. For example, search for the method used to initialize a WKWebView \([`init(frame:configuration:)`](https://developer.apple.com/documentation/webkit/wkwebview/1414998-init)\): ```bash $ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame" 0x5c3ac 77 76 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfC 0x5d97a 79 78 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfcTO 0x6b5d5 77 76 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfC 0x6c3fa 79 78 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfcTO ``` #### Testing JavaScript Configuration For `WKWebView`s, as a best practice, JavaScript should be disabled unless it is explicitly required. To verify that JavaScript was properly disabled search the project for usages of `WKPreferences` and ensure that the [`javaScriptEnabled`](https://developer.apple.com/documentation/webkit/wkpreferences/1536203-javascriptenabled) property is set to `false`: ```text let webPreferences = WKPreferences() webPreferences.javaScriptEnabled = false ``` If only having the compiled binary you can search for this in it: ```bash $ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled" 391 0x0002f2c7 0x10002f2c7 17 18 (4.__TEXT.__objc_methname) ascii javaScriptEnabled 392 0x0002f2d9 0x10002f2d9 21 22 (4.__TEXT.__objc_methname) ascii setJavaScriptEnabled ``` #### Testing OnlySecureContent In contrast to `UIWebView`s, when using `WKWebView`s it is possible to detect [mixed content](https://developers.google.com/web/fundamentals/security/prevent-mixed-content/fixing-mixed-content?hl=en) \(HTTP content loaded from a HTTPS page\). By using the method [`hasOnlySecureContent`](https://developer.apple.com/documentation/webkit/wkwebview/1415002-hasonlysecurecontent) it can be verified whether all resources on the page have been loaded through securely encrypted connections. In the compiled binary: ```bash $ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent" ``` You can also search in the source code or strings the string "http://". However, this doesn't necessary means that there is a mixed content issue. Learn more about mixed content in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content). ### Dynamic Analysis It's possible to inspect the heap via `ObjC.choose()` to find instances of the different types of WebViews and also search for the properties `javaScriptEnabled` and `hasonlysecurecontent`: {% code title="webviews\_inspector.js" %} ```javascript 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()); } }); ``` {% endcode %} Load it with: ```bash frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js onMatch: > hasOnlySecureContent: false ```