These definitions are imported from other sources.
We use the following principles to help evaluate the quality of design decisions.
Without seeking to precisely define “user consent” in this document, users should have control over the sharing of information. User control may manifest itself in a variety of ways, including:
The protocols should not disclose private details of the user’s identity or other sensitive information unless required for operational purposes or by legal or jurisdictional rules.
In general, we seek to minimize friction in the user experience. However, “minimal” does not always mean “none,” especially in the pursuit of user privacy and security.
The payments APIs should provide privacy and security protection that is at least on par with —and in general better than— existing approaches.
This threat model focuses on the following privacy threats that a merchant, a payment handler, or both in collusion, may exploit against a user:
Although this document focused primarily on Web-based payment handlers, it does include some discussion of native payment handlers as well (specifically, on Android).
This threat model does not address the following:
The Payment Request API and Payment Handler API provide a set of capabilities to merchants and payment handlers. The Chrome implementation provides additional capabilities. In the descriptions below we label the former "Spec" and the latter "Chrome".
a
element ignored
The attack vectors enumerated below can be grouped into several themes:
Note:Risk may be lower if user explicitly allowed “no-UI flow” for some use cases. Also consider cross-device payment sheet.
In the detailed analysis below we relate the threats to these themes.
badph.example hosts a badph.js file that does the following:
Chrome already implements the following limits on hasEnrolledInstrument():
Additional strategy ideas:
Same steps as for a web-based payment handler regarding a Web-based zero-click track attack, except:
badph.example hosts a badph.js file that does the following:
badph.example convinces merchant1.example and merchant2.example to serve badph.js via a <script> tag. Then:
This attack is less dangerous than a Web-based zero-click track attack because it requires tricking the user to click on something. On the other hand, request.show() is not subject to throttling like hasEnrolledInstrument().
Note: The steps above refer to a Web-based payment handler, but the same attack can be performed with a native Android payment handler.
Same steps 1-3 as one-click tracking by a payment handler. Because of the storage access mitigation, badph.example’s service worker only has access to 3P storage at this point. Then:
This attack is a minor variation of Web-based payment handler as a zero-click tracker and is less severe because it requires that the attacker to also have access to the server logs of both badph.example and merchant.example. The same mitigation strategies are also effective.
The same attack can be performed by a native Android app based payment handler.
Note: The steps above refer to a Web-based payment handler, but the same attack can be performed with a native Android payment handler.
The effectiveness of this attack to uniquely identify a user depends on three factors:
At the time of writing, the total number of configurations that exists today is C = M \* N + P, where:
This gives a rough estimate of C = 24, i.e. merchant.example can build a 24-bit vector to fingerprint the user, as an upper bound. The actual information should be less because:
So a more realistic estimate may be C = 8 bits of actual information. C will also grow as more payment methods become available (e.g. src, other URL-based payment methods), i.e. increasing P value.
The table below is a model of the “identifying power” of this fingerprinting vector, measured by the number of uses per bucket, as a function of C and Payment Request API user population size (P):
P = 100K | P = 1M | P = 100M | |
---|---|---|---|
C = 8 (today) | 400 | 4000 | 400K |
C = 9 | 200 | 2000 | 200K |
C = 10 | 100 | 1000 | 100K |
C = 11 | 50 | 500 | 50K |
This means that today, when Payment Request API is not yet widely used, with C = 8, hasEnrolledInstrument() can narrow users down to 400-person buckets. This may be small enough to be worrisome.
Chrome throttles hasEnrolledInstrument() to 1 call every 30 minutes per merchant origin. This makes the attack not very useful for websites that a user only visits occasionally because they wouldn’t be able to build up a full bitvector without reasonable amount time.
Time required to build a full bitvector:
Adding more payment methods will make the bitvector longer, increasing its identifying power for a given population size, but increases the time required to build up the vector.
Our conclusion is that, as is, hasEnrolledInstrument() is not a very useful fingerprinting tool.
The hasEnrolledInstrument() quota creates a problem for merchants: if a legitimate merchant wants to offer a “Buy now with wallet1.example” button side by side with a “Buy now with wallet2.example” button, the quota will cause one of the calls to reject and hence not display the button. A proposed solution is to change the quota to allow 1 call per payment method per 30 minutes per merchant origin. Currently page visibility is not used as part of the time quota, so a user who leaves a tab open in the background is prone to this attack.
With per method quota, the limiting factor for a merchant to collect all the bits is only N, which is ~4 in a given country (because M & P can be computed in parallel). Time required to build a full bitvector of equivalent identifying power as C=8 above:
A possible attack:
The origin that hosts a payment handler (e.g. ph.example) can install a service worker and register it as payment handler when a user visits ph.example as a first party. See C6.
Mitigation:
Yes, in the current implementation of Chrome:
Mitigation:
Note: At their 1 April 2020 meeting, WPWG participants expressed strong support for providing consistent skip-the-sheet behavior across browsers.
With some difficulty. So current thought is to move away from seamlessly installed payment handlers and rely on user’s brand recognition to filter out most of illegitimate payment handlers. Longer term, we can also imagine using a safe-browsing type approach: filter out any known bad origins, and use telemetry to detect likely bad actors (e.g. high trigger frequency with zero completion rate, never showing UI, etc.)
TODO: Definition of illegitimate PH, possibly based on behavior that falls within the identified scope.
In Chrome there are currently two cases where the payment handler is not pinged and canmakepayment event returns true:
Going forward, it may be useful to allow user to configure this per payment handler (perhaps as part of the onboarding process) and make this setting more visible. See the payment handler availability clarified proposal.
Possibly. The shipping delegation feature adds ability for payment handler to declare that they handle shipping addresses and/or contact information. But there are two challenges: 1) ultimately all payment information is sensitive 2) a uncooperating payment handler can hide arbitrary data using the method-specific data blob, which is opaque to the browser.