App-Bound Domains
Many applications use WKWebView
as a convenient way to display websites without requiring users to leave the app, referred to as in-app browsing. Although this can provide a great user experience, the powerful features available to developers using WKWebView
allow a hosting app to monitor users across all of the sites they visit within the app.
Powerful WKWebView
features, such as JavaScript injection, event handlers, and other APIs can be used by applications or utility frameworks in intrusive ways to communicate with known trackers seeking to collect and aggregate personal information about users. These tactics can reveal which images a user pauses on, what content they copy/paste, and which sections of pages they reach while scrolling.
For iOS 14.0 and iPadOS 14.0, we want to make it possible for developers to continue offering an in-app browsing experience without exposing users to tracking risks. Today we are introducing App-Bound Domains, a new, opt-in WKWebView
technology to improve in-app browsing by offering greater privacy to users.
App-Bound Domains
The App-Bound Domains feature takes steps to preserve user privacy by limiting the domains on which an app can utilize powerful APIs to track users during in-app browsing. Applications that opt-in to this new feature can specify up to 10 “app-bound” domains using a new Info.plist key — WKAppBoundDomains
. Note that content supplied by the app through local files, data URLs, and HTML strings are always treated as app bound domains, and do not need to be listed.
<plist version="1.0">
<dict>
<key>WKAppBoundDomains</key>
<array>
<string>example1.com</string>
<string>example2.org</string>
...
</array>
</dict>
Once the WKAppBoundDomains
key is added to the Info.plist, all WKWebView
instances in the application default to a mode where JavaScript injection, custom style sheets, cookie manipulation, and message handler use is denied. To gain back access to these APIs, a WKWebView
can set the limitsNavigationsToAppBoundDomains
flag in their WKWebView configuration, like so:
webViewConfiguration.limitsNavigationsToAppBoundDomains = YES;
Setting this flag indicates to WebKit that a WKWebView
will only navigate to app-bound domains. Once set, any attempt to navigate away from an app-bound domain will fail with the error: “App-bound domain failure.” A web view which has this configuration flag and loads an app-bound domain from the WKAppBoundDomains
list, or from local resources like file URLs, data URLs, and strings, will have access to the following APIs:
(void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler
(void)addUserScript:(WKUserScript *)userScript;
window.webkit.messageHandlers
Additionally, an application will have access to the following WKHTTPCookieStore
APIs for accessing cookies for app-bound domains:
(void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;
(void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;
All other WKWebView
instances are prevented from using these APIs, since they are capable of leaking private data. This makes a WKWebView
navigating to domains outside of the small set of app-bound domains work more like SafariViewController
, which has built-in privacy protections like this already.
We will talk more about specific examples and benefits of App-Bound Domains below.
Example Use Cases
We will use five examples to illustrate the ways App-Bound Domains can be adopted for different types of applications:
- UnchangedApp, an application which does not opt-in to App-Bound Domains.
- ShopApp, an application with only self-hosted content.
- SocialApp, an application with an in-app browser.
- BrowserApp, a full web browser application.
- HybridApp, an application with both self-hosted content and an in-app browser.
UnchangedApp
First let’s consider UnchangedApp, which does not opt-in to App-Bound Domains or change its behavior in any way. UnchangedApp will experience pre-iOS 14.0 WKWebView
behavior, with no restricted APIs on any domains. However, the decision to not adopt App-Bound Domains in UnchangedApp could expose its users to tracking risks, for example if UnchangedApp includes third party code that surreptitiously injects script into web views.
ShopApp (self-hosted content)
Let’s look at a simple example of an application, ShopApp, which only serves web content from its own domain, shop.example. ShopApp can opt-in to App-Bound Domains by creating a new array entry in its Info.plist with the key WKAppBoundDomains
. This kind of app can add up to 10 “app-bound” domains as strings in the array. This might be an example of ShopApp’s Info.plist entry:
<plist version="1.0">
<dict>
<key>WKAppBoundDomains</key>
<array>
<string>shop.example</string>
<string>shop-cdn.example</string>
</array>
</dict>
In order for a WKWebView
to use restricted APIs on these domains, ShopApp will also have to initialize the WKWebView
with the following configuration argument:
webViewConfiguration.limitsNavigationsToAppBoundDomains = YES;
ShopApp will now have access to the full spectrum of APIs listed above when browsing on shop.example and shop-cdn.example. Note that the check for app-bound domains only occurs for the top-level frame, so ShopApp will still be able to display third party iframes from domains outside the app-bound set on shop.example.
SocialApp (with in-app browser)
Now let’s consider a social media application, SocialApp, which is used largely as an in-app browser. A SocialApp user might navigate to many different websites using the app, possibly encountering a tracker, tracker.example, during in-app browsing.
Without the protections offered by App-Bound Domains, it is possible that SocialApp is intrusively using in-app browsing to track users by communicating with tracker.example.
If the developers of SocialApp want a better user privacy experience they have two paths forward:
- Use
SafariViewController
instead ofWKWebView
for in-app browsing.SafariViewController
protects user data from SocialApp by loading pages outside of SocialApp’s process space. SocialApp can guarantee it is giving its users the best available user privacy experience while using SafariViewController. - Opt-in to App-Bound Domains. The additional
WKWebView
restrictions from App-Bound Domains ensure that SocialApp is not able to track users using the APIs outlined above.
To opt-in to App-Bound Domains, SocialApp only needs to add an empty WKAppBoundDomains
key to their Info.plist.
<plist version="1.0">
<dict>
<key>WKAppBoundDomains</key>
</dict>
Since SocialApp does not need any restricted APIs, no WKWebViewConfiguration
arguments are necessary.
Due to the asynchronous nature of the web, a SocialApp developer could see different errors if trying to use restricted APIs and navigate to non app-bound domains in the same WKWebView
. If a WKWebView
in SocialApp uses a restricted API before any navigations occur, and then tries to navigate to a domain outside of the set of “app-bound” domains, the navigation will fail with the error “App-bound domain failure.” Conversely, if SocialApp first navigates to a non-app-bound domain then tries to use a restricted API, the API call will fail.
BrowserApp (exclusively browsing the web)
Another application, BrowserApp, is used exclusively for browsing the web. BrowserApp has previously received permission to take the managed entitlement com.apple.developer.web-browser, which signifies its purpose as a full web-browser. All WKWebView
instances for BrowserApp will therefore have unrestricted API access on all domains. BrowserApp will not need to add a WKAppBoundDomains
value to their Info.plist or make any changes to the way they initialize WKWebView
.
HybridApp (both self-hosted + in-app browser)
Finally, let’s look at a more complex example. HybridApp is an application which offers in-app browsing to its users, but also requires restricted API use for WKWebView
instances on its own domain, hybrid.example. HybridApp is a combination of ShopApp and SocialApp, and you should read and fully understand those examples first before considering HybridApp.
HybridApp’s Info.plist might look like this:
<plist version="1.0">
<dict>
<key>WKAppBoundDomains</key>
<array>
<string>hybrid.example</string>
</array>
</dict>
HybridApp needs to inject JavaScript on hybrid.example, so it might create a WKWebViewConfiguration
with the specific argument: webViewConfiguration.limitsNavigationsToAppBoundDomains = YES;
.
HybridApp can now navigate to hybrid.example and successfully inject JavaScript:
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://hybrid.example"]];
[webView evaluateJavaScript:script completionHandler:^(id value, NSError *error) {
if ([value isEqual:@"Successfully injected JavaScript"])
// …
}];
Say HybridApp tried to use this WKWebView to navigate to shop.example. Since shop.example is not an app-bound domain, this will result in a failed navigation.
Instead, HybridApp can create a different WKWebView
with no limitsNavigationsToAppBoundDomains
configuration flag. HybridApp can use this new WKWebView
to navigate to any domain, including app-bound domains. However, any attempts to call restricted APIs will fail.
Other Options
The App-Bound Domains feature was created to allow for in-app browsing without sacrificing user privacy.
Prior to iOS 14, the only way to protect web content inside applications was to use SFSafariViewController for general web content, or ASWebAuthenticationSession for authentication purposes. We still think SafariViewController and ASWebAuthenticationSession represent the best way to protect user data, because they are views hosted outside of the application, making it impossible for applications to view or interact with the content of those views. If the developer only wishes to display web content as a convenience to the user, or if they only wish to support a web-based authentication flow, SafariViewController and ASWebAuthenticationSession continue to be the best choices.
Intelligent Tracking Prevention in WKWebView
Additionally in iOS 14.0 and macOS Big Sur, Intelligent Tracking Prevention (ITP), is enabled by default in all WKWebView
applications. To learn more about how ITP protects users against web tracking, checkout this documentation on the topic.
In some extreme cases, users might need to disable ITP protections, for example when relying on web content outside of the app developer’s control. Applications can signal the need to allow users to disable ITP by adding a Purpose String for the key NSCrossWebsiteTrackingUsageDescription
to the app’s Info.plist. When present, this key causes the application’s Settings screen to display a user control to disable ITP. The setting cannot be read or changed through API calls.
Note that applications taking the new Default Web Browser entitlement always have a user control in Settings to disable ITP, and don’t need to specify the NSCrossWebsiteTrackingUsageDescription
key in their Info.plist.
Feedback and Bug Reports
If you find that this feature in any way doesn’t work as explained, please file a WebKit bug at https://bugs.webkit.org and CC Brent Fulgham and Kate Cheney. For feedback, please contact our web evangelist Jon Davis.