Manifest: Security Risks in Web App Off-scope Navigation

Created on 3 Dec 2018  Â·  12Comments  Â·  Source: w3c/manifest

Dear Web Platform Working Group,

We’re a security research group at Indiana University Bloomington. Recently we studied the latest published version of Web App Manifest (Living Document) [3] (called the Doc in this report ) and found that Section 4 Navigation scope lacks sufficient security consideration. A user agent (or implementor) following the recommendation in section 4 Navigation scope when handling off-scope navigation, may put Web App (e.g., Progressive Web App [4], aka. PWA) users at high risks of phishing attacks.

The problem

According to the “SHOULD” level [5] recommendation for off-scope navigation in Section 4 [1] of the Doc, if a Web App navigates to an off-scope URL, “the user agent SHOULD show a prominent UI element indicating the document URL, or at least its origin, including whether it is served over a secure connection. This UI SHOULD differ from any UI used when the document URL is within scope, in order to make it obvious that the user is navigating off scope”. In our research, we believe that this recommendation is insufficient in its security considerations. In particular, when a malicious Web App is in “fullscreen” or “standalone” display mode [2], the recommended prominent UI in the context of off-scope navigation can be counterfeited by the Web App. More specifically, the malicious Web App may show a counterfeit UI indicating that the active URL/webpage is off-scope (e.g., at google.com or facebook.com to perform third-party authentication), while indeed it is still within the scope and counterfeiting the off-scope URL/webpage. We also find that the recommended “prominent UI” implemented by popular mobile browsers is usually simple (see Figure 1) and trivial to counterfeit by a typical Web App.

A more concrete attack scenario

A possible attack leveraging the problem above could take the following steps:

  1. A victim user launches a malicious PWA[4] of domain attack.com and is to navigate off to Google/Facebook/Amazon for some reasonable business (e.g., single sign-on, shopping).

  2. However, the malicious PWA indeed navigates the user to a phishing and within-scope webpage pretending to be Google/Facebook/Amazon.

  3. Upon navigation, the PWA could show a fake “prominent UI”, on behalf of the user agent, misleadingly informing the victim user that he/she is navigated to Google/Facebook/Amazon. It’s natural for the victim user to believe this “prominent UI” is generated by the user agent. Consequently, the victim user incorrectly thinks he/she is navigated to Google/Facebook/Amazon, and leaks sensitive information (e.g., login credentials, personal or private information) to the malicious PWA.

We attached a demo for this attack, check out here

The root cause

As a security protection, when a Web App is navigated off-scope, users rely on the user agent’ prominent UI to know which origin he/she is visiting. But the problem is, a Web App in “fullscreen” or “standalone” display mode can control the UI of the full display area and make a misleading counterfeit “prominent UI”, which is hard to differentiate from a genuine one created by the user agent.

The Web App, though in “fullscreen” or “standalone”, still runs inside the user agent. The malicious Web App misplaces victim users’ trust in the counterfeit “prominent UI”, while the victim users assume the counterfeit UI is created by the trusted user agent.

Recommendation of revision of the Doc

The recommended “prominent UI” should be hard to counterfeit. While a static UI is relatively easy to counterfeit by a “fullscreen” or “standalone” Web App, a potentially robust and secure way to show the “prominent UI” is through an animated transition into the native user agent app, which itself shows the “prominent UI”. As an example on Android, instead of showing the “prominent UI” directly in the activity of the Web App, it could be secure to switch to the mobile browser which then shows the “prominent UI”. Showing the “prominent UI” directly inside the Web App is subject to our reported phishing attacks.

Reference:
[1] Out of scope navigation security consideration,
https://www.w3.org/TR/appmanifest/#navigation-scope-security-considerations

[2] Display modes, https://www.w3.org/TR/appmanifest/#display-modes

[3] Web App Manifest Living Document (W3C Working Draft 13 November 2018), https://www.w3.org/TR/appmanifest/

[4] Progressive Web App, https://developers.google.com/web/progressive-web-apps/

[5] Key words for use in RFCs to Indicate Requirement Levels, https://tools.ietf.org/html/rfc2119

Editorial clarification

Most helpful comment

Ultimately, I think we have to treat this as a product decision that each user agent can make individually. There doesn't have to be agreement in the spec about how internal links are opened. Firefox could, for example, open all links in a browser tab and never allow off-scope pages to be viewed inside the app.

I think it's reasonable to work towards adding a feature to the manifest like stay_in_app as a hint, where developers can say "I need these URLs to stay in the app for my app to work properly", and then put "SHOULD" or even "MUST" text in the spec to keep those URLs inside the app, and have "MAY" text for other URLs. Then, hypothetically, Chrome could ignore stay_in_app and open all off-scope URLs inside the app, while Firefox opens stay_in_app URLs inside the app but other URLs in a browser. Both would be compliant.

All 12 comments

Hi,

Thanks for your report. This is a well-known issue but essentially not actionable, since the whole point of "display": "standalone" is to hide the browser UI, any site installed in such a way can simply recreate fake browser UI inside its relatively bare window.

We got this same report on the Chromium issue tracker yesterday and I'll paste the response from the Chromium side by @dominickng:

This vulnerability boils down to: once installed, any PWA may phish the user because they are displayed without an omnibox.

We have a number of mitigations in place for this. Installing a PWA requires that the user explicitly consents to a prompt that contains the origin of the site, its title, and the icon. This consent stage must be triggered by a user gesture, and is regarded as the point of trust for the origin.

Once installed and running, standalone PWAs have a persistent notification in the system tray that shows their origin.

This is mostly an issue for individual user agents, as it affects their UI, which can't be specified by normative language in the spec. The concrete suggestion here is to upgrade the non-normative recommendation to UI that is "hard to counterfeit", which may be worth adding to the spec, but as mentioned above, not really actionable.

As an example on Android, instead of showing the “prominent UI” directly in the activity of the Web App, it could be secure to switch to the mobile browser which then shows the “prominent UI”.

We actually had that behaviour in the spec previously (see #701). After a lot of debate, we changed it to require not opening the site in the separate browser (because it breaks the normal navigation flow of the web). User agents are required to open links in the app context.

Changing this requirement wouldn't really help. A full stand-alone app on mobile would simply be able to spoof the browser UI and the user would think they're in the full-screen browser.

Basically, there is no way to make "unspoofable" UI if we give the app control over the entire window/screen, and that's the whole point of PWAs. I'm going to close this because it's non-actionable on the spec. Feel free to continue speaking with individual browser vendors about improving their UI.

The comment you cite does not address the issue for off-scope navigation. Can you explain why out-of-scope content absolutely requires use of the entire screen? Fake OAuth pages are definitely the main attack vector here, and have been from the beginning with installed browser apps using redirect OAuth flows.

Hi, mgiuca

Thank you for the timely response. We’d like to make a bit clarification here.

We think currently there’s a design pitfall for user agents that support Web Apps. Specifically, when a Web App takes the entirety of the display area, it’s practically misleading to users if the user agent (e.g., Chrome) is still designed to display the Prominent UI inside the display realm of the PWA in certain circumstances. Specifically, the users cannot tell whether the Prominent UI is from user agent or the Web App. When this Prominent UI is for critical security notification (i.e., informing the current domain during off-scope navigation), such misleading will cause security consequences, e.g., our reported phishing attacks.

From the Spec perspective, we make following two suggestions for your review.
The Spec makes it clear that the user agent’s UI component/notice, if inside the display area of a (fullscreen) Web App, could be practically counterfeited. If the UI component/notice is for critical security purposes, security consequences such as phishing attacks may occur.
The Spec may suggest a secure design principle: it should be practically easy for users to judge whether any UI component/notice inside the display area of Web App is owned by the Web App or user agent.

One possible implementation of the suggested design principle could be, any UI component/notice inside the display area of Web App should only be owned by the Web App. When a user agent wants to show users any critical UI notice, guide the user to the user agent’s separate activity and display it there. After the display, guide the users back to the Web App, so as to eliminate any breaking of the user experience. This is indeed a further explanation of our original suggestion, which is never meant to be a opposition to #701.

Response to your other comments.

In response to your comment

“A full stand-alone app on mobile would simply be able to spoof the browser UI and the user would think they're in the full-screen browser”,

we believe the Operating System should have the responsibility to make it clear what app the user is running. Indeed, on iOS when a user double presses the Home button, he/she would be able to see the real identity of the app running in the foreground, no matter whether this app is spoofing other apps’ UI or not. Similarly, on Android through the Recents Screen [1], users could also reliably know which app is running in the foreground. It indeed happened on Android that a malicious app spoofed the UI of other apps, but it’s Android’s responsibility to fix each reported bug and make sure users always reliably know the real identity of running apps and activities. Similarly, we think it’s the user agents’ responsibility to make users reliably known the owner (Web App or the user agent) of any critical UI component/notice in the context of Web App. We suggest the Spec clarify this responsibility for user agents.

Please let us know if questions.

[1] https://developer.android.com/guide/components/activities/recents

@skddc:

The comment you cite does not address the issue for off-scope navigation. Can you explain why out-of-scope content absolutely requires use of the entire screen? Fake OAuth pages are definitely the main attack vector here, and have been from the beginning with installed browser apps using redirect OAuth flows.

Sorry if I wasn't clear. Out-of-scope content absolutely should not take up the whole screen. We have a SHOULD (recommendation) in the spec that the user agent show "show a prominent UI element indicating the document URL, or at least its origin" when out of scope.

The problem here is not with out-of-scope pages being able to spoof another site. The problem is that in-scope pages, which by definition have no browser UI (otherwise that would defeat the purpose of installable websites), can spoof the user agent's out-of-scope security UI to pretend to be a different off-site URL. That's much less scary, since only a site that the user has explicitly chosen to install can perform this spoofing attack.

@PWAuser:

Specifically, the users cannot tell whether the Prominent UI is from user agent or the Web App. When this Prominent UI is for critical security notification (i.e., informing the current domain during off-scope navigation), such misleading will cause security consequences, e.g., our reported phishing attacks.

Yes, we've acknowledged that this is an issue. It's just difficult to do anything about it when the whole point of this feature is to give the site (which the user has specifically chosen to install, and thus has placed a certain degree of trust in) as much screen real estate as possible. Whether this can be mitigated depends heavily on the user agent.

One possible implementation of the suggested design principle could be, any UI component/notice inside the display area of Web App should only be owned by the Web App. When a user agent wants to show users any critical UI notice, guide the user to the user agent’s separate activity and display it there.

Right, but the problem is if the web app owns all the pixels on the screen, then there is no way to display any browser UI once navigated off-scope, other than to reclaim some of those pixels back from the app. By definition, if the UA conditionally claims pixels from the app, the app can spoof that UI when the UA is not claiming those pixels. On mobile, this is especially hard, since the standalone app takes up all of the available space. On desktop, apps typically have a title bar, which we may be able to place this information inside of, but there's very little space there.

I think this discussion could certainly be fleshed out in the security considerations section, but we can't really make any recommendations. I'll reopen this for the purpose of adding non-normative text to Section 4.1.

Thanks very much, mgiuca. We think we have consensus for the most part.

Designing a general solution may not be trivial, but we think the security risk and goal should be clear to UA vendors and readers of the Spec. In particular, we suggest the security goal be made clear that, it should be practically easy for users to judge whether any UI component/notice in the context of Web App is owned by the Web App or UA.

In terms of actionable solution, we would like to make one little bit clarification here. We think a possible guideline could be, the UA should not reclaim any pixels back from the (standalone) Web App. Specifically, the UA never displays any UI component/notice inside the display area of Web App. When a UA wants to show any critical UI notice, switch the user to the UA app (which is now in the foreground) and display it there. (The Operating System should have the responsibility to make it clear what app is running in the foreground.) After the display, guide the users back to the Web App, so as to eliminate any breaking of the user experience.

This tentative solution is open to discussions or evaluation. But making the security risk and goal clear will bring in solutions from the community.

Discussed with spec editors.

We've agreed to add some non-normative text to security considerations section outlining that this is a potential risk. (We would acknowledge the reporters.) We do not propose any normative text to rectify this, as it would break the whole concept of a PWA.

FWIW I believe this issue can in fact be addressed, though likely after CR as it requires adding new features to the specification and/or implementations of the specification.

Specifically:

  1. Deep linking
  2. More complex scope definition

This is a long time ago now, but in 2014 I carried out a study of the Alexa Top 500 websites, Chrome Web Store and Firefox Marketplace.

We subsequently spent a lot of time discussing potentially more sophisticated ways of defining the scope of an application, which I won't go into here in depth but which never quite reached a conclusion.

The way I believe this issue can be (mostly) fixed is:

  1. A more sophisticated definition of scope with one or more lists of paths, including from different origins, and possibly differentiating between URLs that should be captured by the application context and URLs which should just stay in the application context if navigated from it
  2. Have user agents fully support deep linking such that out-of-scope navigation from inside an application context do get kicked out to a new browsing context (or different application context) but in-scope navigation in a browsing context also get captured by a corresponding application context
  3. Keeping the current requirement of a rudimentary URL bar in the application contexts for some edge cases

The phishing risk can never be completely eliminated because a standalone app can always fake a third party login page from its own origin, but it can certainly be greatly reduced by having out-of-scope navigation happen entirely in the browser UI which has much more sophisticated protections in place.

I personally really dislike the current popover style solution for out-of-scope navigation and think a better user experience would ultimately be to kick all out-of-scope navigation out to the browser, but also have installed application contexts capture in-scope navigation from the browser.

Note that we are working on #748 to add non-normative note and acknowledge the reporters of this issue.

@benfrancis It sounds like your suggestion would remove the ability to display an off-scope page within the application context (except in "some edge cases"?). I agree that would solve the phishing risk, but at the cost of functionality that developers want (i.e., not kicking users out of their app). It would also break key workflows, such as authentication (maybe these are the edge cases you're talking about).

It sounds like your suggestion would remove the ability to display an off-scope page within the application context (except in "some edge cases"?). I agree that would solve the phishing risk, but at the cost of functionality that developers want (i.e., not kicking users out of their app). It would also break key workflows, such as authentication (maybe these are the edge cases you're talking about).

My suggestion is to define a more sophisticated mechanism for defining scope to enable both the main application scope (which would capture navigations from outside the application scope for deep linking purposes) but also stay_in_app scope (where a whitelist of scopes, including from different origins, would remain in the application context if navigated to from inside the application context). Names to be subject to extensive bikeshedding.

For example...

{
  "start_url": "https://calendar.google.com/calendar/r",
  "scope": {
    "capture": ["/calendar"],
    "stay_in_app": ["https://accounts.google.com"] 
  }
}

In the above example, accounts.google.com is whitelisted by the calendar application so doesn't kick the user out to the browser, but navigating to the rest of the web does. This strikes the balance between what developers want (not kicking the user out to the browser to authenticate) and what I believe many users want (doing their web browsing in their choice of fully featured web browser, rather than a dozen different apps with simplified read-only URL bars).

The edge cases I mentioned are more about whether off-origin navigations marked as stay_in_app may still want to display the origin to the user in some way as is currently done.

A more complex proposal included both whitelists and blacklists for defining scope, to exclude certain paths from the navigation scope. This is a simplified version of that.

OK that makes more sense. But would it still be possible for developers to ask for * (the whole URL space) to be stay_in_app? I think that's what a lot of apps are going to want (e.g., social media apps that want to open articles within the app, rather than going to a web browser).

what I believe many users want (doing their web browsing in their choice of fully featured web browser, rather than a dozen different apps with simplified read-only URL bars)

I totally agree, as a user. I'm sick of apps keeping me in a tiny web browser within the app instead of opening my real browser. But, ultimately, I think we've struck the right balance between users and developers, because:

a) Developers have control over the default behaviour (i.e., the left click). If a developer wanted to keep you in the app, they could anyway by loading the page in an iframe. This just makes the experience more consistent, and gives the user better control (e.g., the user agent can always give the user controls to access the URL and ping out to a real browser),
b) Users have ultimate control. A middle-click or right-click-open-in-tab would open in a real browser. And the URL bar gives control.

I think we'd be going backwards if we prevented developers from being able to keep users inside the app by default.

@mgiuca wrote:

But would it still be possible for developers to ask for * (the whole URL space) to be stay_in_app?

No, this is specifically designed to prevent that.

But, ultimately, I think we've struck the right balance between users and developers

As both a user and developer, I respectfully disagree :) But I am just one person. I would love to see some user research on this. My impression is that decisions like this tend to be weighted towards developers, who are the people who comment on and write the specification.

I think we'd be going backwards if we prevented developers from being able to keep users inside the app by default.

I disagree. I think this trend really breaks the natural flow of navigating the web and just enforces the siloed app model of smartphone operating systems on the whole web.

Ultimately, I think we have to treat this as a product decision that each user agent can make individually. There doesn't have to be agreement in the spec about how internal links are opened. Firefox could, for example, open all links in a browser tab and never allow off-scope pages to be viewed inside the app.

I think it's reasonable to work towards adding a feature to the manifest like stay_in_app as a hint, where developers can say "I need these URLs to stay in the app for my app to work properly", and then put "SHOULD" or even "MUST" text in the spec to keep those URLs inside the app, and have "MAY" text for other URLs. Then, hypothetically, Chrome could ignore stay_in_app and open all off-scope URLs inside the app, while Firefox opens stay_in_app URLs inside the app but other URLs in a browser. Both would be compliant.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sealabr picture sealabr  Â·  4Comments

frlinw picture frlinw  Â·  7Comments

mfulton26 picture mfulton26  Â·  4Comments

marcoscaceres picture marcoscaceres  Â·  9Comments

aarongustafson picture aarongustafson  Â·  8Comments