Privacy – WebKit https://webkit.org Open Source Web Browser Engine Tue, 31 Jan 2023 17:27:14 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 Allowing Web Share on Third-Party Sites https://webkit.org/blog/13708/allowing-web-share-on-third-party-sites/ Tue, 31 Jan 2023 17:27:14 +0000 https://webkit.org/?p=13708 As of Safari Technology Preview 160, it is no longer possible to use the W3C’s Web Share API with third-party sites within an iframe without including an allow attribute. All browser vendors agreed to this change as part of the W3C’s standardization process, and it is being rolled out in all major browser engines (including Chrome, Edge, Firefox, on mobile and desktop browsers).

The Web Share API allows web developers to enable the native sharing functionality of a device, such as sharing a link via email or social media. Prior to this change, the API could be used on any website within an iframe without restriction. However, due to concerns about privacy and security, browser vendors at the W3C have decided to limit the use of the API to only those sites that have explicitly been given permission to use it.

Web developers must now include an allow attribute in the iframe HTML element to use the Web Share API within an iframe on a third-party site. The attribute accepts a value of web-share and optionally the origin of the site that is allowed to use the API.

<iframe allow="web-share" src="https://example.com">
</iframe>

or

<iframe allow="web-share https://example.com" src="https://example.com">
</iframe>

Without the allow attribute, the API will throw an exception and will not function within the iframe. The syntax of the allow attribute is defined by the W3C’s Permissions Policy specification. You can learn more about the syntax on MDN.

This change is a necessary step in protecting user privacy and security. It helps ensure that the Web Share API is only used by sites that the developer signals is trustworthy. However, that means web developers will need to make some code changes to continue using the API within third-party iframes.

]]>
Private Click Measurement: Conversion Fraud Prevention and Replacement For Tracking Pixels https://webkit.org/blog/12566/private-click-measurement-conversion-fraud-prevention-and-replacement-for-tracking-pixels/ Mon, 11 Apr 2022 16:18:08 +0000 https://webkit.org/?p=12566 Welcome to the fourth feature update on Private Click Measurement, our proposed web standard for measuring advertising in a privacy-preserving way. More precisely three major and two minor updates to PCM, all available in iOS/iPadOS 15.4 and macOS Monterey 12.3.

Major updates:

  • Conversion fraud prevention. This enables merchant websites to sign unlinkable tokens and get proof in the attribution report that it was triggered by a trustworthy conversion event.
  • Replacement for third-party tracking pixels. Merchant websites can now trigger a conversion event through a same-site pixel which removes the need to call any third-parties in PCM.
  • Measurement of clicks in cross-site iframes. Many publishers want to isolate ads on their site in cross-site iframes. Now clicks in such iframes can be measured too.

What is Private Click Measurement?

Private Click Measurement, or PCM, is a proposed web standard for measuring the effectiveness of click-through advertising in a privacy-preserving way. It allows for 8 bits of data on the click source site to be combined with 4 bits of data on the click destination site to measure which clicks are driving conversions. The combined 8+4 bits of data is sent to both the click source and destination in an attribution report that doesn’t carry any user or device identifiers. The net result is a report that says “Someone who clicked ad X on website A later converted with value Y on website B.”

PCM was made available as a beta in May 2019, and then shipped 2021 in iOS/iPadOS 14.5 and in Safari 14.1 on macOS. Its privacy-preserving nature means it can be used without getting the user’s permission to track according to AppTrackingTransparency.

Conversion Fraud Prevention

In July 2021 we presented our beta feature for click fraud prevention in PCM. It allows the click source website to sign an unlinkable token at the time of the click navigation, which results in a signed token being included in the resulting attribution report. Today we present the same capability for the click destination website.

PCM’s triggering event is a redirect to the well-known path .well-known/private-click-measurement/trigger-attribution/. Now that redirect supports a query string parameter called attributionDestinationNonce. If a nonce is included in the redirect, it triggers WebKit to ask the click destination server to sign an unlinkable token.

Step 1: Generate an RSA Key Pair

Unlinkable tokens require a public key for token generation and validation, and a corresponding private key for signing. PCM supports three different RSA key sizes: 2048, 3072 and 4096 bits. The expected encoding of the public key is a Base64URL encoded (using RFC4648 section 5) SPKI with the RSA-PSS OID and the parameters corresponding to the RSABSSA IETF draft. Examples of such key encodings are included in an earlier blog post.

Step 2: Add the Query Parameter attributionDestinationNonce

The triggering event should look like this to opt in to conversion fraud prevention:

https://site.example/some/sub/resource/
… redirects to …
https://site.example/.well-known/private-click-measurement/trigger-attribution/11?attributionDestinationNonce=ABCDEFabcdef0123456789

The attributionDestinationNonce is only in place to help the click destination server know the context for which it’s signing an unlinkable token. When the click destination server is asked to sign an unlinkable token, it’ll get the attributionDestinationNonce and can make a decision as to whether the triggering event was trustworthy or not.

The attributionDestinationNonce needs to be a Base64URL encoded 128-bit/16-byte value. Any smaller or larger value will cancel the issuance flow of the fraud-prevention signature. Any non Base64URL encoded value will also cancel the token transaction. Web Inspector will log a warning if the attributionDestinationNonce is malformed.

Step 3: Respond to a Request for Your Public Key

The browser and any validating party needs to be able to fetch your public key at any point in time from this well-known location: https://clicksource.example/.well-known/private-click-measurement/get-token-public-key/.

Replacement For Third-Party Tracking Pixels

Previous versions of PCM required HTTP requests on the click destination site to go to the click source site. This was designed to enable reuse of existing cross-site tracking pixels that no longer carry cookies under tracking prevention.

For new adoption of PCM, for instance onboarding a new publisher site where ads are shown, there’s no need to add such legacy pixels. Longer term, we want to remove support for triggering events through cross-site tracking pixels since even though they don’t carry cookies, they do ping third-party domains that may be categorized as trackers. Many websites want to be completely tracker-free and we’re happy to be able to support that with PCM while still allowing for click measurement across websites.

Click destination sites can now signal a triggering event through a same-site pixel. It looks like below.

Same-site subresource request to:
https://clickDestination.example/triggeringEventRedirect/21

… redirects to same-site well-known location:
https://clickDestination.example/.well-known/private-click-measurement/trigger-attribution/21?attributionSource=https://clickSource.example

The above redirect tells PCM to schedule an attribution report if there is a pending attribution from clickSource.example.

Why not a JavaScript API to trigger attribution without cross-site pixels, you may ask? There are two reasons for that:

  • The standards conversation in W3C Privacy CG told us that some click destination sites have a policy against placing third-party scripts on their websites because of the risky dependencies they introduce. We think that’s a very valid concern, not in the least for user privacy. However, those same click destination sites do not have the same strict policies against pixels. We can given them the API functionality they need by introducing same-site pixels. They can be triggered through actual image elements or through a JavaScript Fetch.
  • We continue to consider it very important that websites should not be able to tell at page load time whether or not the user has PCM (or other ad measurement) features enabled. This is to protect the user’s right to a choice. Pixel APIs don’t reveal if the triggering event is accepted or not. A JavaScript API could be made to not reveal that info either but pixel APIs just do that.

Measurement of Clicks in Cross-Site Iframes

One of the first PCM change requests from the web community was to allow for measurement of clicks that happen in cross-site iframes. This would allow for measurement of advertising isolated in iframes. We’re happy to announce that we now support that. Note that the resulting attribution report is still sent to the two first-party websites, often referred to as the publisher and the merchant.

WebIDL Attributes Now In Camelcase

JavaScript access to PCM’s anchor tag attributes now requires camel-casing. This is the result of interoperability work in web standards. Here’s how it looks:

<a id="test" href="https://clickDestination.example" attributionsourceid=40 attributiondestination="https://clickDestination.example"></a>
<script>
    const anchorTag = document.getElementById("test");
    anchorTag.attributionSourceId …;
    anchorTag.attributionDestination …;
</script>

Encoding Update for Token Keys

When sending your public key to the browser, it needs to be a 2048, 3072 or 4096 bit RSA public key wrapped with an RSAPSS OID in ASN1 format. See “Step 1: Generate an RSA Key Pair above.” Those bytes then need to be base64 encoded with the URL and filename safe alphabet. Examples of such key encodings are included in an earlier blog post.

]]>
PCM for In-App Direct Response Advertising https://webkit.org/blog/12042/pcm-for-in-app-direct-response-advertising/ Tue, 16 Nov 2021 17:06:04 +0000 https://webkit.org/?p=12042 Private Click Measurement (PCM) can now be used for in-app direct response advertising using SFSafariViewController. Try it out in our iOS 15.2 beta.

What is PCM?

PCM is a proposed web standard for measuring the effectiveness of click-through advertising in a privacy-preserving way. It allows for 8 bits of data on the click source site to be combined with 4 bits of data on the click destination site to measure which advertising is driving sales. The combined 8+4 bits of data is sent to both the click source and destination in an attribution report that doesn’t carry any user or device identifiers. The net result is a report that says “Someone who clicked ad X on website A later converted with value Y on website B.”

PCM shipped in iOS/iPadOS 14.5 and in Safari 14.1 on macOS. Its privacy-preserving nature means it can be used without getting the user’s permission to track according to AppTrackingTransparency.

What is SFSafariViewController?

SFSafariViewController is a ready-built in-app web browser on iOS/iPadOS with full-fledged Safari features such as Reader, AutoFill, Fraudulent Website Detection, content blocking, and bookmarks. User activity and interaction with SFSafariViewController are not visible to your app which means users can safely browse the web in it and you do not need to secure data between your app and SFSafariViewController.

On Direct Response Advertising

Our introductory blog post on PCM featured two important FAQ entries on app-to-web advertising – on the subject of taking the user to the device’s browser and on the subject of an in-app experience.

When to Take the User To the Device’s Browser

PCM app-to-web in iOS 14.5 had to take the user to the device’s browser. This was designed to support re-engagement. Stored clicks in PCM are valid for 7 days and customers who are ready to take action only after a few hours or days, will most likely go find the merchant website in their browser. They’ll either look up the tab where they left off, use a bookmark they might have saved, use their search provider to find the right webpage, or enter the website’s address directly in the URL bar.

For the stored click data to be readily available when the user re-engages in this fashion, the initial click needs to take the user to their browser. This is still true going forward.

When to Handle the Tap In-App

Another form of click-through advertising is called direct response. In such cases, the user is not expected to think about converting for an extended period of time, but rather take action directly on the webpage they land on. It could be a limited offer or a product priced so that the user doesn’t feel like they have to think it over.

Developers and advertisers have told us they want to be able to provide more of an in-app experience for direct response advertising, rather than take the user to the device’s browser. They want a seamless in-app experience for users who tap on an ad, check out the product page, decide to buy or not, and then want to go back to the hosting app.

Our intro blog post covered this request in the FAQ section, and we said “We are interested in this but don’t have a solution yet.” Today we have a solution. PCM is now capable of supporting in-app advertising with new API for SFSafariViewController.

PCM App-to-Web with SFSafariViewController

SFSafariViewController provides a great in-app browsing experience where the user can store Safari bookmarks that can sync across devices, has access to autofill of credentials and payment card info, and Apple Pay. It is simply a great place to take the user as part of direct response advertising.

Ephemeral Clicks to Prevent Click Fraud

PCM will only store click data and schedule an attribution report if the user triggers a conversion in the SFSafariViewController that they opened through the click. A tap which navigates to a website in SFSafariViewController without a matching triggering event will not be stored. As a result, an instance of SFSafariViewController can only hold one non-converted click at a time, whereas all converted clicks will be stored and result in reports.

This ensures that a hosting app cannot speculatively store clicks in its SFSafariViewController for fraudulent reasons. It also ensures that this use of PCM really is geared toward direct response advertising.

Attributions are Per Hosting App

Taps in different apps navigating the user to the same advertised website do not affect each other. Every app gets its own attribution. Again, this feature is for direct response advertising so a customer who buys a product twice based on ad clicks in two different apps will generate two attribution reports.

Attribution Reports Don’t Require Your App to Run

One particular challenge for the kind of delayed attribution reporting PCM uses is what to do if the user doesn’t use the hosting app frequently or doesn’t use it around the time when the report is supposed to be sent out. We have made sure that pending attribution reports from PCM app-to-web with SFSafariViewController are sent independent of if the hosting app where the click happened is running or not.

The API

As shown in our introductory blog post on PCM, apps can already use Private Click Measurement with Safari by putting a UIEventAttribution on a UISceneOpenExternalURLOptions and using it with UIScene’s openURL:options:completionHandler:.

In iOS 15.2 beta, a new attribute of type UIEventAttribution is added to the existing class SFSafariViewControllerConfiguration:

@available(iOS 15.2, *)
  @NSCopying var eventAttribution: UIEventAttribution?

You can optionally use it when opening a URL in SFSafariViewController, like this:

func didTapOnAdWithIdentifier(advertisementIdentifier: UInt8, url: URL) {
    let attribution = UIEventAttribution(
        sourceIdentifier: advertisementIdentifier, 
        destinationURL: url, 
        sourceDescription: "Ad for toy XYZ.",
        purchaser: "Toy Example Company")
    let configuration = SFSafariViewController.Configuration()
    configuration.eventAttribution = attribution
    present(SFSafariViewController(url: url, configuration: configuration), animated: false)
}

When an UIEventAttribution is part of the configuration, SafariViewService checks that a tap on an UIEventAttributionView preceded the opening of SFSafariViewController to guarantee click-through attribution. Then the information from the UIEventAttribution object is checked before it’s given to WebKit for processing.

Private Click Measurement works as previously from that point.

Debugging Your App

See “Testing and Debugging” in our introductory blog post on PCM for how to turn on PCM Debug Mode.

Make sure to restart your app after enabling PCM Debug Mode with SFSafariViewController. If you are having trouble getting PCM Debug Mode to turn on or off, try restarting the device.

Please Provide Feedback

We really appreciate all the developer and ad tech feedback we’ve received so far on Private Click Measurement. Prioritizing PCM for in-app measurement was the result of such feedback. There are three ways for you to continue to tell us what you think:

  • The standards proposal repository in the W3C Privacy Community Group for anything related to the specified web parts of PCM, i.e. feedback on the proposed standard as it would work in any web engine.
  • https://bugs.webkit.org for anything specific to the WebKit implementation of PCM.
  • Apple Feedback Assistant for anything specific to the UIKit or SFSafariViewController APIs for PCM.
]]>
PCM: Click Fraud Prevention and Attribution Sent to Advertiser https://webkit.org/blog/11940/pcm-click-fraud-prevention-and-attribution-sent-to-advertiser/ Thu, 29 Jul 2021 17:00:39 +0000 https://webkit.org/?p=11940 Earlier this year, Safari was the first browser to ship a proposed web standard for measuring advertising in a privacy-preserving way – Private Click Measurement, or PCM. Today we’re happy to announce three major updates to PCM, available in our iOS/iPadOS 15 and macOS Monterey betas:

  • Attribution reports also sent to click destination.
  • Click fraud prevention with unlinkable tokens.
  • IP address protection for attribution reports.

All of it is covered in Kate’s WWDC session. There are also two minor updates to naming and data types so look out for those below.

Let’s jump in.

Quick PCM Refresher

PCM allows for measurement of clicks which navigate the user from one website to another or from an iOS app to a website. The website or app where the click happens is called the click source, and the website the user is navigated to is called the click destination.

Attribution Reports Also Sent to Click Destination

We’ve designed PCM’s attribution reports to ensure they make sense to both the click source and the click destination, i.e. no opaque IDs that can only be understood or decoded by one party. PCM now sends the resulting attribution report to both click source and destination to help advertisers validate data and be in control of their ad measurement. The two reports are sent independently with a 24-48 hour delay (each report gets its own delay within those 24-48 hours). Since PCM reports already contain information about both click source and destination, there is no new privacy risk involved in sending it to both places (they could have already shared this information).

PCM’s Debug Mode (see “Testing and Debugging” in our previous blog post) sends them independently too but with the shorter 10 second delay, i.e. one after 10 seconds and one after 10+10=20 seconds.

Click Fraud Prevention With Unlinkable Tokens

We mentioned in our introductory blog post on PCM that fraud prevention is a top request for the feature and we’re super excited to announce that optional unlinkable tokens for click fraud prevention are now supported. The cryptographic operations required for blinding and unblinding the tokens are only available on iOS/iPadOS 15 and macOS Monterey so no such tokens will be provided by clients on older operating systems. However, PCM will still work and send attribution reports if you opt-in to tokens in a Safari version that doesn’t support them. Also, unlinkable tokens for click fraud prevention are only available in PCM web-to-web for now.

An unlinkable token provides a privacy-preserving proof, generated by the click-source and the client, of a unique click event on the attributed link. Unlinkable tokens are implemented using RSA blind signatures, a well-studied cryptographic construction that guarantees unlinkability between the moment the token is getting signed and the verification of the resulting signature, through blinding and unblinding operations that are performed on the client side. In contrast with some other cryptographic token constructions, blind signatures provide public verifiability, allowing both the click source and click destination to verify the authenticity of the click event on the click’s source website.

This is how you as a developer of the click source website opt in to click fraud prevention with unlinkable tokens:

Step 1: Generate an RSA Key Pair

Unlinkable tokens require a public key for token generation and validation, and a corresponding private key for signing. PCM supports three different RSA key sizes: 2048, 3072 and 4096 bits. The expected encoding of the public key is a Base64-encoded SPKI with the RSA-PSS OID and the parameters corresponding to the RSABSSA IETF draft.

Step 2: Add the New Link Attribute attributionsourcenonce

Add a nonce on the click source side:

<a href="https://shop.example/productABC.html" attributionsourceid=3 attributiondestination="https://shop.example" attributionsourcenonce="ABCDEFabcdef0123456789"></a>

The attributionsourcenonce is only in place to help the click source server know the context for which it’s signing an unlinkable token. The click source is intended to uniquely and ephemerally identify a specific click event in the eyes of the server. When the server is asked to sign an unlinkable token it’ll get the attributionsourcenonce and can make a decision as to whether the click event was trustworthy or not.

The attributionsourcenonce needs to be a Base64URL encoded 128-bit/16-byte value. Any smaller or larger value will cancel the issuance flow of the fraud-prevention signature. Any non Base64URL encoded value will also cancel the token transaction. Web Inspector will log a warning if the attributionsourcenonce is malformed.

Step 3: Respond to a Request for Your Public Key

The browser and any validating party needs to be able to fetch your public key at any point in time from this well-known location: https://clicksource.example/.well-known/private-click-measurement/get-token-public-key/.

Your server’s response should look like this, with its key Base64URL-encoded:

{
  "token_public_key": "MIICUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAg8AMIICCgKCAgEAoGhU5Mgsbb51ZbJVPHSgf8c93TJdtkxeKfyxQ5fCpwE2Fe9xJ7tByExdGKj4XO+HFi7npmtEPzR4cRXdsAL7YcH5UXNbVhXmVcbFCBXks+Ih+jqLwfNac0wPLG5K1Zzhf1gZ++JBzVjw87zvqpWrzzxviuV//0sn/u7f01E1OdaD83110fhfiXp/Ex62Q2uhcek0hqbqEvyKlLVBOjlJFJc2FLyw+l8+9xd7GcX1ZRyPx4lITvYG7KIbSMrFTfuQNOyJf4DlO97qq08R6Utl249AnBfLe3ZDbWBnl0fDOwkJgBmbaa7EnRlQ3p6Ir2SY1hNTnzW+p2ceytIMYwTMSES7+j21oeTUC+OmcC/5g05AgxROzUJPZdyY33m4Q7lqkHkLAYtdN2TVCP79MuswS+fJJQOD/dDCqq/hk0MySLCbnUGe5lyFBoO5vBMH5k38LjSQuN6jfP7quYA6cOONzmn842eLT61tIjRoX2czeUJrSmx89SfY8WnFE2fhk9G52cXp6L2Vzr5IV7rOws3ZPw+RnjKnZZaejs0bKGOXC1+jl+u4A5ip55ohlUjm7lvDtKFAeJ7gajJBtiNnq3s3m/IMkv7ztCQpv0pBxst6MmvNOO0jOQvYkzQbGooI1/qjeDup0BYY67xxyNRaA9V4CKEJ7j/hznrAmjSiz0LSqTkCAwEAAQ=="
}

Step 4: Receive Token Signing Request, Sign, and Respond

After the browser has requested your public key, it will send your server a signing request with an unlinkable token to this well-known location: https://clicksource.example/.well-known/private-click-measurement/sign-unlinkable-token/.

The request body looks like this:

{
  "source_engagement_type": "click",
  "source_nonce": "ABCDEFabcdef0123456789",
  "source_unlinkable_token": "BC16WmucFh4u8pnfKX+3oRLyu2Bkc39UhMDt8ncOHQsd1Joo5ZslNVNdYajMQaNcOdlCpb6weGOE0R9nHtMjzMyUyJWx1T6ADsJsuFwfHvaVGY6v4fOHRc5P0WReOaiBgkGnDwOdG38rQcKk9lAWHCrcSSCsPTuA6L2W81C9/+0fUED+HY8Dqh1q7AkK/dJbsH0oA1nkYPHhPwghdp4QHZzTPSIHtQt/kNUXjT6Z2UPPEUelZNuEYKHUYyKrsKHZhP/2SJmJzJRrV3wb8chKxOpLmQPRkuDBRLIjqe+o7d2IHe4/5Gt1S3m/jrNSggUb8hekHD9C6WLcZ2tVI2A4ALV4clkJAOMtOH819KEd7fQgCT2kRjvz5EkhVto7WHhToCAbLSFCUjucMWqG7o8iRjy6BGvz09GdSNYBSeALwKm+QjOtsuh+sHMh/H0ngqRbT6RgRa8a+4ejsMCeoqZ52H9X6B+yJywT9eLqcKjDnAk2CJR+EOt4Q6FeMqkfUz5SJtGkjGv1PMnXw3PqT3SzuL3B6d3nW7DySkWkR1xMcZnIC0vW7gMgj9O5+Jq3WQh1XlNqkCraPZIbDFGeCIs4LHMRfTA8yzWFEL1CwdrMDjrmpqtzENHGxFHRr/TKozc3v/vGKc2rE9EfhWSAATFSIjeyUUByUCySR3rCzBssaR0=",
  "version": 2
}

The source_nonce is the attributionsourcenonce that was provided in the link and the source_unlinkable_token is the Base64URL-encoded token to sign.

If you deem this is a valid request, your server performs the BlindSign operation of the RSABSSA draft, which is a RSA private key operation on the browser-provided source_unlinkable_token and returns the blind signature as a Base64-encoded output labeled “unlinkable_token" in a response like this:

{
  "unlinkable_token": "QfvIa2RF16zPcXmsbhrsX7ZcUDAk1rtoOcrqE1XoaK7MUrsLNeolZwzNhkOUSSwJKiXPJpam4q1/teU379uiizDFTLPPaPXqxoEq7L/+QjkGmh5lrmFYSbN475Lm+hb9k1rrPT+ek4PDQ3PZpk35h/cB36iE4cf0/F72Iuf4gnn2S/gGL4oZDb4Yi9njY186TzAZ3E9/1bGKiw6qg1gFD92i7yEm//KdqlcwTrdvaMkqzZtD7KmCzkaub46bVIokLwOuDa2SCj9dDaBVRmcHIAtF174nARQjKkp3do/q8pdZh+syeEUYmnpl+qe+yG9+AlSWQXI+3zW6KZ0PbFAQ/hrmqTU9v3vDOdKFi/gr321A6XeUtL4dQxr/Anw0opLxX/lcwsa6gFiWnpMqBv7x+xnhJBFJI+0USUxq2gXVonhXBg/HVMhrW+eEbqMtrVF23JkXaYX1eaQrV9lfFf6LgHo+7GAVbHF6Y7J4IwfwuvksDfJ1/9axdO5jKQ58gCAlH0zpVgMxNmBcAgCBUv3ebZmrKNzNFSjsodBQ30FmKMPkT1l0eWlaDBz5QIG4y1Pc+ZKW9tCV0rFTg4FMoeN8aayUkTCKGpnRgmfO03uQMeQbuPOkAndjyXQyhevOTespEo9X6Z1KI+w7dVmPL/8oG8Mx+2bic6eszWprP/Kef64="
}

Step 5: Respond to Another Request for Your Public Key

When it’s time for the browser to send the attribution report, it will again fetch the public key from your server and make sure that it’s the same public key. This is how PCM makes sure that the server doesn’t use user-specific keys. Note that this second request for your public key may happen multiple days after the click you are measuring and that the browser will discard the attribution data if it receives a mismatching key.

There is currently no support for managed key renewal. We intend to support responses with two public keys – one current and one decommissioned. That way, the browser can compare with both and allow attribution with tokens that were signed with the private key matching the decommissioned public key.

Step 6: Receive and Validate the Attribution Report

The resulting attribution report will contain a signed secret token (source_secret_token) that your server has never seen before and that your server cannot link to its corresponding unlinkable token (source_unlinkable_token). However, the signature you created with your private key will be valid for the secret token, effectively proving to you that you deemed this a trustworthy click some time back. Make sure to validate the signature.

Since the click destination receives attribution reports too, they should validate the token as well.

The report will look like this, with the source_secret_token and source_secret_token_signature Base64URL-encoded:

{
  "source_engagement_type": "click",
  "source_site": "clicksource.example",
  "source_id": 201,
  "attributed_on_site": "shop.example",
  "trigger_data": 10,
  "version": 2,
  "source_secret_token": "7JgS5aIQPUm9T5DcT2a91NC1lt2xq5bLjuaJi4A/Wbg=",
  "source_secret_token_signature": "ThyNW13Z7DTVSj/U8+5oWyG73bskeB2ZtmyG+tZRbuX216mK2F7wgv8piQEFxjDC49O9fPP7DFovcJbGOx3JR7zS7fDq3pYOKz/LkF8I2DkLz9jDcgXxgddMRfFsG8ud6FyEtmESiFgF23Nfqnn4JrhC4luDb7JceOdFsNWtXTURYeVcnARhKlcQ8h8Gs0zTCTGz2LkhwOHUlRYUTnqy5Ng9DiK4Rb9XSaTTPFPK2VJ7PNDmVFtvj1uc2OSxO8AJu9FYF4pv0wQjXjBKy00BF6Qm3m7vZXIwu7pTHBbXlb7DpJ2/15OblNEZrbS0BbXUzv8gqhz6MqmstltZdDiQZHRNDXabmPX7Rm1NRiy5XBr2oF+YBcSHJ0xV3YEH3XoeGN2McBoZCQ7CLhhMcDQLGVBv0L05Wp5rwausxd6Yerf01ebedk5D7RlQmrQ0lMo6fnqm6/F9gEHil2axA8zB/xThPD4ZQ0ARHkGWQYraQzGq5Xj65CIa1yV174iDcf6ZP18Hvkj1VcQIderLg0oMI6FOFzSYYeWJR0vKSc6C+y6jAX4dPPS+1uKRMBBijdv/4H9GazAQHhXLGKxaRgbLg+mLXKG4I9YoPVgU/gc/ePeTAyXFs+EgTi5ExpTF2Klv10E8HTtYLAO76FQVjnDNB98dq+XIbFHMuVKzAaBFr7s="
}

IP Address Protection for Attribution Reports

Safari 15 already hides the devices IP address from known trackers so if PCM is communicating with a known tracker, that protection is there. On top of that, PCM now protects all of the requests it makes that don’t happen very close in time to a linkable event in a first-party context, i.e. requests that don’t have a high likelihood of being matched based on time with very recent non-protected requests. The device IP address is hidden by sending requests through a two-hop relay which makes sure that Apple doesn’t get to see things like the attribution data. Learn more about this technology in the WWDC session “Get ready for iCloud Private Relay.”

PCM Debug Mode skips IP address protection so that you can test locally.

Attribute Name and Data Type Change

Our introductory blog post on PCM also mentioned that there will likely be changes to naming due to the ongoing efforts to harmonize as much as possible with Google’s alternative proposal Click Through Attribution Reporting API.

The PCM naming and data type changes in Safari 15 are:

  • The click destination is now stated in the anchor element’s attributiondestination attribute (used to be attributeon).
  • attributionsourceid is now an unsigned long, i.e. a non-negative integer (used to be a string).

This means that a PCM link now looks like this:

<a href="…" attributionsourceid=3 attributiondestination="https://shop.example"></a>

Feature Enabled and Disabled Should Be Indistinguishable

Our continued work in this space has taught us that it’s important to design ad measurement features so that websites cannot tell if the user has the feature enabled or not, at least not at page render time. The reason why is to offer users a real choice. If a webpage can tell that the user has chosen to disable ad measurement, it may pressure the user to enable it. We encourage other browser vendors in this space to think about this important aspect of user agency, especially in light of the multi year effort to make the web work well without third-party cookies (as a user setting or as the default).

FAQ

Q1: My triggering event is not accepted. How should I debug?
A1: Make sure the HTTP request to https://clickSource.example/.well-known/private-click-measurement/trigger-attribution/ is done as the result of a same-site HTTP redirect, e.g. a clickSource.example to clickSource.example redirect. Otherwise it won’t be accepted as a triggering event. There are two reasons for this requirement:

  • Support for legacy tracking pixels. With a redirect, nothing needs to change on the click destination site.
  • Server-side control. By requiring a server-side redirect, we enable the domain owner to be in control of when a triggering event is fired. If it was just a HTTP request, any JavaScript on the destination site could fire triggering events. The redirect is not a fool proof system but it offers at least one checkpoint of control.

Q2: I’m not receiving attribution reports in my testing. How should I debug?
A2: Attribution reports are sent over HTTPS to the registrable domain of the click source and click destination. There needs to be a valid TLS certificate for each of those domains and the server needs to accept a request to the well-known location.

Q3: Will Apple add support for unlinkable tokens for triggering events on the destination site?
A3: Yes, that is our intention.

Q4: Will these enhancements be available in Safari 15 on older versions of macOS?
A4: Attribution reports also sent to click destination will be available in Safari 15 across the board. However, click fraud prevention with unlinkable tokens relies on cryptographic functionality that is only available starting with iOS/iPadOS 15 and macOS Monterey.

]]>
Updates to the Storage Access API https://webkit.org/blog/11545/updates-to-the-storage-access-api/ Wed, 10 Feb 2021 17:00:29 +0000 https://webkit.org/?p=11545 The Storage Access API allows third-party web content to ask for permission to get access to its unpartitioned storage, typically in order to authenticate the user. In the case of Safari and WebKit, using the Storage Access API enables cookie access under Intelligent Tracking Prevention.

This blog post covers two changes to the Storage Access API in Safari and WebKit as well as a how-to guide on adoption based on questions we’ve been asked the last two years.

Changes to API Functionality

iOS and iPadOS 14.5, and macOS Big Sur 11.3 betas feature two sought after changes to the Storage Access API in Safari and WebKit – per-page storage access and support for nested iframes. Both of these changes were driven by the standards process in W3C Privacy CG.

Per-Page Storage Access

If a request for storage access is granted to embedee.example, access is now granted to all embedee.example resource loads under the current first party webpage. This includes sibling embedee.example iframes but also other, non-document resources.

Nested Iframes Can Request Storage Access

Imagine a webpage embedding a cross-site iframe from embedeeOne.example which in turn embeds a cross-site iframe from embedeeTwo.example which makes the latter a so called nested iframe. As of this release, nested iframes such as embedeeTwo.example are also allowed to request storage access. Note that we may require first parties to explicitly delegate this capability through Permissions Policy at a later stage. Mozilla has expressed an interest in such control.

How To Use the Storage Access API

For the purposes of this guide we will use the domains social.example for the embedded content in need of cookie access and news.example as the first party website embedding social.example.

First, Cross-Site Iframes Call the API

The Storage Access API is called from inside cross-site, or third-party, iframes. You don’t have to call the API if your website is first party and first party websites cannot call the API on behalf of third-parties.

How-To #1: Meet and Greet the User as First Party

If you want to make use of the Storage Access API as a third-party, you first need to take these steps as a first party:

  1. Make sure you are using regular browsing mode, i.e. not Private Browsing. We will cover Private Browsing at the end of this guide.
  2. Take the user to your domain as first party. This is your website showing itself and giving the user a chance to recognize your brand and domain name. Recognition is important since the prompt for storage access features your embedded iframe’s domain. In our example, this is taking the user to a webpage with social.example in the URL bar, either though a navigation or a popup.
  3. Have the user interact (tap, click, or use the keyboard) with your website as first party. This tells the browser that the user has actually seen and used the site. Note: Navigating to and from your website in a redirect without user interaction does not count. Formally, WebKit’s requirement is user interaction as first party the last 30 days of browser use. Being granted storage access through the Storage Access API counts as such user interaction. In our example, this is having the user tap/click on the webpage with social.example in the URL bar.
  4. Set cookies when you are first-party. This establishes the website as “visited” for the purposes of the underlying cookie policy. Third parties without cookies cannot set cookies in Safari and never have since Safari 1.0 in 2003. This means you cannot use the Storage Access API as third-party until you have set at least one cookie as first party. In our example, this is setting cookies for social.example with social.example in the URL bar.

The above requirements are there to make sure the sometimes 50-100 embedded third-parties on a single webpage cannot all prompt the user for storage access, only the ones the user has visited and interacted with can.

How-To #2: Use the Storage Access API as Third Party

Once you have had the user interact with your website as first party and have set cookies as first party, you are ready to make use of the Storage Access API.

  1. In shipping Safari, your cross-site iframe that is about to request storage access must be a direct child frame of the top frame. Nested iframes can request storage access as of iOS 14.5 and macOS 11.3 (currently in beta).
  2. Make your cross-site iframe call document.hasStorageAccess() as soon as it’s rendered to check your status. Note: Don’t call this function upon a user gesture since it’s asynchronous and will consume the gesture. Once the user gesture is consumed, making a subsequent call to document.requestStorageAccess() will fail because it’s not called when processing a user gesture. In our example this is social.example‘s iframe.
  3. If document.hasStorageAccess() returns false, your iframe doesn’t have storage access. Now set an event handler on elements that represent UI which requires storage access and make the event handler call document.requestStorageAccess() on a tap or click. This is the API that requires a user gesture. In our example this is social.example‘s iframe calling the API.
  4. Render the page with your cross-site iframe. Tap or click on an element with an event handler in the iframe. In our example this is rendering a page from news.example with thesocial.example‘s iframe and clicking on an element in the social.example iframe’s document.
  5. If the user has not yet opted in to storage access for social.example under news.example there will now be a prompt. Choose “Don’t Allow” in the prompt. Tip: Don’t choose “Allow” yet because it’ll be remembered and you’ll have to delete browser history to reset it. If you are not getting the prompt, you either have not gotten user interaction as first party and set cookies for social.example yet (see How-To #1) or you have already chosen “Allow” earlier which is remembered.
  6. Test the behavior for the “Don’t Allow” case. You can do it repeatedly. Do it until you’re happy with how your code handles it. Note that when the user chooses “Don’t Allow” in the prompt, their user gesture is consumed and any further API calls in your iframe that require a user gesture will have to get the user to tap or click again. We’ve deliberately designed it this way to make sure that an explicit deny from the user doesn’t trigger further privileged API calls from the iframe. The user should at this point be able to continue with other things.
  7. Now tap or click the iframe again and this time choose “Allow” in the prompt. This should open up cookie access on resolution of the promise.
  8. Test the behavior for the “Allow” case. Note that when the user chooses “Allow” in the prompt, their user gesture is preserved and any further API calls in your iframe that require a user gesture can go right ahead. We’ve deliberately designed it this way so that when you get access to cookies and note that the user is not in the desired state, such as not logged in, you can open a popup or navigate them to your website without further user gestures. In our example this would be a popup or navigation to social.example.
  9. Now reload the webpage. This will reset your per-page storage access. Tap or click the iframe to trigger the call to document.requestStorageAccess(). This should open up cookie access without a prompt since the user has already opted in and that choice is remembered.
  10. Finally test the flow in Private Browsing Mode. In that mode, the user must interact with your website as first party (see How-To #1) in the same tab as where you later request storage access as third-party. This is because Private Browsing Mode uses a separate ephemeral session for each new tab the user opens, i.e. the state of those tabs are separate. The rest should work the same as in regular mode.
]]>
Introducing Private Click Measurement, PCM https://webkit.org/blog/11529/introducing-private-click-measurement-pcm/ Mon, 01 Feb 2021 19:30:33 +0000 https://webkit.org/?p=11529 This blog post covers a new feature called Private Click Measurement, or PCM, for measuring ad clicks across websites and from iOS apps to websites. It is part of iOS and iPadOS 14.5 betas.

Motivation and Goals

Classic ad attribution on the web is done with cookies carrying user or device IDs. Such attribution constitutes cross-site tracking which WebKit is committed to preventing. Websites should not be able to attribute data of an ad click and a conversion to a single user as part of large scale tracking.

At the same time, we want to support measurement of online advertising. PCM achieves this tradeoff by sending attribution reports with limited data in a dedicated Private Browsing mode without any cookies, delaying reports randomly between 24 and 48 hours to disassociate events in time, and handling data on-device.

The Feature in a Nutshell

  • A new, on-by-default feature called Private Click Measurement, or PCM, for privacy-preserving measurement of ad clicks across websites and from iOS apps to websites in iOS and iPadOS 14.5 betas.
  • An 8-bit identifier on the click source side, which means 256 parallel ad campaigns can be measured per website or app.
  • A 4-bit identifier on the conversion side, which means 16 different conversion events can be distinguished.
  • Fraud prevention via unlinkable tokens will be coming.

A Proposed Standard

We first proposed privacy-preserving measurement of ad clicks in May 2019. Since then the proposal has changed name to Private Click Measurement and been discussed extensively in the W3C Privacy Community group, both through meetings and on GitHub.

A proposal needs two independent implementations to be on track to become a web standard. This means another browser such as Firefox, Brave, Chrome, or Edge needs to independently implement PCM before it can move further along the standards track. We are working with them to get there.

Nevertheless, we are happy to be the first browser to enable a proposed web standard for measuring advertising!

On By Default

You may ask why we are enabling PCM by default before there is a second independent implementation and before we’ve added the fraud prevention mechanism discussed in W3C Privacy CG. The reasons are:

  • Early access. We recognize the need for early access so that advertisers, websites, and apps can adopt the technology, analyze real data, tune their measurement, and report any issues to us.
  • Equal access. We want to provide everyone with the opportunity to test and use this technology from the get-go. An alternative would be to only run it with selected partners but we have opted for an open approach.
  • Attribution data is stable. Fraud prevention tokens will be added and naming of data labels might change, but the functionality and attribution data is stable, namely 8 bits on the click source side and 4 bits on the attribute-on side. Thus, full scale tests of PCM are meaningful and useful at this point.

Web-to-Web Click Measurement

PCM web-to-web is the case covered by the proposed standard, i.e. a user clicks a link on a webpage, is navigated cross-site, and up to seven days later, there’s a signal on the destination website saying it would like attribution for any previous clicks that took the user here.

For the purposes of the examples below, we assume the click happens on a website called social.example and the click navigates the user to shop.example.

The Click Side

Links that want to store click measurement data should look like this:

<!-- Link on social.example --> 
<a href="https://shop.example/product.html" 
   attributionsourceid="[8-bit source ID]"
   attributeon="https://shop.example">

  Markup

</a>

The two mandatory attributes are:

  • attributionsourceid: The 8-bit attribution source ID, allowed to be between 0 and 255. This was earlier referred to as the ad campaign ID but since PCM is not technically tied to advertising, it was decided in the standards discussion that its attributes and key names should not use advertising terms.
  • attributeon. The click destination website which wants to attribute incoming navigations to clicks. Note that PCM only uses the registrable domain or eTLD+1, i.e. there is no separation based on subdomains. This is so that the destination cannot be set up as https://janeDoeTracking.shop.example to track user Jane Doe.

If the click indeed navigated the user to the attributeon website, the attributionsourceid is stored as a click from social.example to shop.example for 7 days.

Note that this data is not accessible to websites. It’s silently stored in the browser.

The Triggering Event

To trigger click attribution, the “attribute on” website has to make an HTTP GET request to the website(s) where it is running click-through ads. This way of doing it is intended to support existing “tracking pixels” and make adoption easy. In our example this would be the shop.example site making an HTTP GET request to social.example. For a more modern way of triggering attribution, see the Future Enhancements section.

The HTTP GET request to social.example triggers attribution if it is redirected to https://social.example/.well-known/private-click-measurement/trigger-attribution/[``4-bit`` trigger data]/[optional 6-bit priority]. Note: The first beta lacks the /trigger-attribution path component since this was a very recent decision in the standards group.

The two URL path parameters are:

  • Trigger data. This is a 4-bit value between 00 and 15 that encodes the triggering event (note the mandatory two digits). This was earlier referred to as the conversion value but again, PCM is not technically tied to advertising so it doesn’t use advertising terms.
  • Optional priority. This is a 6-bit value between 00 and 63 which allows multiple triggering events to result in a single attribution report for the event with the highest priority (again, note the two digits). For instance, there might be multiple steps in a sales funnel where each step triggers attribution but steps further down the funnel have higher priority. This value only controls which trigger data goes into the attribution report and is not part of the attribution report itself. You may ask why this isn’t a 4-bit value like the trigger data. The reason is to support easy changes to what’s being measured without having to remap several trigger-data-to-priority pairs. Triggering events 00-15 may start out as mapped to priority 00-15 but then the shop owner wants to drill into events 5-7. With the extra bits, it’s easy to assign triggering events 05-07 to priority 20-22 so as to focus attribution reports to those.

Once a triggering event matches a stored click, a single attribution report is scheduled by the browser to be sent out randomly between 24 and 48 hours later, or the earliest time thereafter when the browser is running. As long as an attribution report has not yet been sent, it can be rescheduled based on a triggering event with higher priority.

The Attribution Report

PCM attribution reports are sent as HTTP POST requests to /.well-known/private-click-measurement/report-attribution/ on the website where the click happened, in our example https://social.example/.well-known/private-click-measurement/report-attribution/. Note: The first beta lacks the /report-attribution path component since this was a very recent decision in the standards group.

The report is in JSON and looks like this:

{
  "source_engagement_type" : "click",
  "source_site" : "social.example",
  "source_id" : [8-bit source ID],
  "attributed_on_site" : "shop.example",
  "trigger_data" : [4-bit trigger data],
  "version": 1
}

Notes on the non-obvious key-values above:

  • source_engagement_type is always “click” for PCM. This field allows for future use of this report mechanism for other types of attribution such as view-through.
  • version signals to the receiving end which version of the attribution feature this is. You should expect this number to be increased when fraud prevention tokens are added or something else about the mechanism is changed. This allows concurrent versions to work in parallel and provides a signal to developers that there may be things they need to change or adopt on their side.

App-to-Web Click Measurement

This is exciting – we’re adding the capability to measure ad clicks from iOS and iPadOS apps to Safari!

Many advertisers in apps want to take the user to their website where the user can buy a product or sign up for a service. This is exactly the kind of ad PCM app-to-web allows them to measure.

The Click Side

The only thing that differs from PCM web-to-web is on the click side which is in an iOS app. To adopt this technology you need to do this:

  1. Add a URL to where you want PCM’s ad attribution reports to be sent when ads are clicked in your app. You do this under the key NSAdvertisingAttributionReportEndpoint in your Info.plist. The naming of this endpoint is deliberately not tied to PCM. Potential future ad measurement reports associated with apps may use this URL with a differing well-known location if appropriate. Note that the subsequent HTTP redirect to trigger attribution needs to go to this website.
  2. Populate and add the new UIEventAttribution to the options of your call to openURL:. See below for what fields you need to enter in UIEventAttribution.
  3. Overlay the parts of the click-through ad that will trigger navigations to websites with the new UIEventAttributionView. This view only serves as a checkpoint for Apple’s code on-device to check that a user gesture happened before the navigation. The view does not consume the gesture and you are free to decide whether or not to navigate to a website even if the gesture happened on one of these views. A user gesture is required for your UIEventAttribution object to be forwarded to the browser as part of the call to openURL:. Note that PCM app-to-web is so far only supported in Safari and only on iOS and iPadOS. We intend to add WebKit API to enable other default browsers to be the destination of PCM app-to-web too.

UIEventAttribution

This is the optional data structure you submit in your call to openURL: when you want to measure clicks:

open class UIEventAttribution : NSObject, NSCopying {
    open var sourceIdentifier: UInt8 { get }
    open var destinationURL: URL { get }
    open var reportEndpoint: String? { get }
    open var sourceDescription: String { get }
    open var purchaser: String { get }
    public init(sourceIdentifier: UInt8,
                destinationURL: URL,
                sourceDescription: String,
                purchaser: String)
}
  • sourceIdentifier is the same as PCM’s attributionsourceid attribute for links. Allowed values are 0-255.
  • destinationURL is the same as PCM’s attributeon attribute for links but it should be a full URL with protocol. The report will be sent to the URL’s registrable domain (eTLD+1) and over HTTPS.
  • reportEndpoint will be picked up by Apple code from your info.plist’s NSAdvertisingAttributionReportEndpoint. As you can see, the init function does not take this parameter. This is where PCM will send any subsequent ad attribution reports. The reason why it need to be stated in the static Info.plist is so that it cannot be used as a tracking vector by dynamically submitting user-specific reporting URLs such as janeDoeTracking.example.
  • sourceDescription is a human-readable description of the content that was tapped. This string should be no longer than roughly 100 characters and can be localized according to the context. It will not be seen by Apple or the destination website. Instead it’s intended to be able to show to users what ad click data they have stored.
  • purchaser is a human-readable name or description of the purchaser of the content that was tapped, typically the ad buyer. This string should be no longer than roughly 100 characters and can be localized according to the context. It will not be seen by Apple or the destination website. Instead it’s intended to be able to show to users what ad click data they have stored.

UIEventAttribution Sample Code

func openAdLink() {
    let adURL = URL(string: "https://shop.example/tabletStandDeluxe.html")!
    let eventAttribution =
        UIEventAttribution(sourceIdentifier: 4,
                           destinationURL: adURL,
                           sourceDescription: "Banner ad for Tablet Stand Deluxe.",
                           purchaser: "Shop Example, Inc.")

    // If using scene lifecycle.
    let sceneOpenURLOptions = UIScene.OpenExternalURLOptions()
    sceneOpenURLOptions.eventAttribution = eventAttribution
    self.view.window?.windowScene?.open(adURL,
                                        options: sceneOpenURLOptions,
                                        completionHandler: nil)

    // If using application lifecycle.
    let appOpenURLOptions: [UIApplication.OpenExternalURLOptionsKey : Any] = [
        .eventAttribution: eventAttribution
    ]
    UIApplication.shared.open(adURL,
                              options: appOpenURLOptions,
                              completionHandler: nil)
}

UIEventAttributionView

UIEventAttributionView is the view that is placed over the tappable content, typically an ad. It’s used by the system to verify that a user gesture has occurred.

open class UIEventAttributionView : UIView {
}

The view is invisible and very lightweight. The simplest use case is to create one of these views and stretch it over your entire tappable content. You can also place multiple over a single piece of content if you for instance want to create specific tappable areas.

To ensure your UIEventAttributionView works correctly:

  • Ensure isUserInteractionEnabled is false. This is the default value for this view and ensures the view doesn’t consume events which would otherwise go to the content beneath it.
  • Ensure there are no views placed on top of the event attribution view. The user should be tapping this view for it to count as a user gesture for the purposes of PCM app-to-web.
  • Ensure your tap handling occurs on a touch up event. This automatically occurs if your content is tapped in response to a UITapGestureRecognizer firing or at the .ended state of a UILongPressGestureRecognizer.

UIEventAttributionView Sample Code

func addEventAttributionView() {
    // Create an event attribution view.
    let eventAttributionView = UIEventAttributionView()

    // Place it over your ad however you'd like.
    eventAttributionView.translatesAutoresizingMaskIntoConstraints = false
    adView.addSubview(eventAttributionView)
    NSLayoutConstraint.activate([
        adView.topAnchor.constraint(equalTo: eventAttributionView.topAnchor),
        adView.leadingAnchor.constraint(equalTo: eventAttributionView.leadingAnchor),
        adView.trailingAnchor.constraint(equalTo: eventAttributionView.trailingAnchor),
        adView.bottomAnchor.constraint(equalTo: eventAttributionView.bottomAnchor)
    ])
}

Testing and Debugging

WebKit has an experimental feature called Private Click Measurement Debug Mode. You’ll find it under Develop–>Experimental Features on macOS and under Settings–>Safari–>Advanced–>Experimental Features on iOS and iPadOS. When you enable this mode and restart Safari, reports go out a mere 10 seconds after the triggering event instead of 24-48 hours later. This allows quick turnaround in testing and debugging.

The debug mode also enables debug output in Web Inspector’s console. This output will show up by default in a later beta.

Remember to disable debug mode once you’re done testing.

Future Enhancements

As is always the case with web standards, proposed or established, there are enhancement requests, corner cases, and a need to evolve the specification as the platform progresses. Below is a list of prominent and relevant issues that may show up as changes to our implementation of PCM in upcoming releases. Please take part on GitHub if you have input.

  • Fraud prevention with unlinkable tokens, GitHub issue #27. A proposed solution was presented to W3C Privacy CG in May 2020. It will use what is traditionally called blinded signatures (we call them unlinkable tokens). The intention is to offer websites to cryptographically sign tokens which will be included in attribution reports in a format that makes it impossible to link them back to the event when they were signed. These tokens serve as proof to the report recipient that they trusted the events involved (link click and attribution trigger) without telling them which events.
  • Modern JavaScript API for triggering event instead of legacy tracking pixels, GitHub issue #31. The intent here is to let a JavaScript call serve as the triggering event instead of redirected tracking pixels. This will remove the requirement for making third-party requests all together.
  • Attribution reports to advertisers too, GitHub issue #53. We have expressed that we’d like the attribution report to be sent to both the click source and the advertiser site. However, this sparked a conversation on sending reports to designated third-parties and you can read and join that conversation in GitHub issue #57.
  • Support PCM links in nested iframes, GitHub issue #7. This is about measuring click-through ads served in cross-site iframes. Since subsequent attribution reports will be sent to the first-party click source site, it’s not clear how that first party should control click measurement requested on its behalf. Part of this conversation covers not just serving of ads by third parties but also reporting to such third-parties. The privacy risk of such a scheme is explored in GitHub issue #57.

Misuse or Use Together With Tracking May Lead To Blocking

PCM is intended to support privacy-preserving measurement of clicks across websites or from apps to websites. It is not intended to be used to track users, events, or devices across those contexts.

If PCM is being misused for tracking purposes or being used in conjunction with unrelated means of tracking users, events, or devices, we may block the offending party from using PCM and potential future measurement features.

FAQ

  • What about PCM web-to-app? We are interested in this but don’t have a solution yet.
  • What about view-through ad attribution? We are interested in this but don’t have a privacy-preserving solution yet.
  • Is there a reason why the click has to take the user to the device’s browser? Yes. Stored clicks are valid for 7 days. Let’s assume that the user doesn’t trigger attribution right after they click but want to think about it first. When they choose to re-engage a few hours or days later they will most likely go to their browser and either look up the tab where they left off, use a bookmark they might have saved, use their search provider to find the right webpage, or enter the website’s address directly in the URL bar. For the stored click data to be readily available when the user re-engages in this fashion, the initial click needs to take the user to their browser since PCM data just like other website data is not shared between browsers and WebViews. In short: The user’s browser is the most likely place where delayed click-through attribution will happen.
  • Does use of PCM app-to-web require the app to be granted permission to track according to AppTrackingTransparency? No.
  • How do users delete stored clicks? Stored clicks are deleted when they delete website data.
  • Can users opt out of PCM? Yes. There is a new Safari privacy setting for privacy-preserving ad measurement. If the user has opted out, no click metadata will be stored and no attribution reports will be sent out.
  • Is PCM enabled in Private Browsing Mode? No.
  • What is the maximum number of parallel ad campaigns per source website or source app? 256, with the actual value being between 0 and 255.
  • What is the maximum number of triggering events I can distinguish? 16, with the actual value being between 0 and 15.
  • What is the maximum time between a click and a triggering event to still get attribution? 7 days.
  • Can I use PCM app-to-web with WebViews? No. Apps have too much control over WebViews for a feature like PCM to be able to protect the data.
  • Can I use PCM app-to-web with SFSafariViewController? We are interested in this but don’t have a solution yet.
  • Can other default browsers on iOS and iPadOS participate in PCM app-to-web? It is our intention to add such an API at a later point. Please let us know if you are interested.
  • Where can I provide feedback? Please file any web-facing issues or issues with the attribution report mechanism directly to WebKit: https://bugs.webkit.org. Please use Feedback Assistant for any issues with UIKit APIs or the Info.plist integration: https://developer.apple.com/bug-reporting/.

Thank You

We’d like to thank the W3C Privacy Community Group for all the work filing issues, suggesting changes, and engaging with us on this work. Please continue to do so as we move forward. Also, a big thank you to the engineers who’ve helped implement this feature – Anant, Kate, Jon, Chris, Jonathan, Chris, and Glen.

]]>
CNAME Cloaking and Bounce Tracking Defense https://webkit.org/blog/11338/cname-cloaking-and-bounce-tracking-defense/ Thu, 12 Nov 2020 18:30:01 +0000 https://webkit.org/?p=11338 This blog post covers several enhancements to Intelligent Tracking Prevention (ITP) in Safari 14 on macOS Big Sur, Catalina, and Mojave, iOS 14, and iPadOS 14 to address our latest discoveries in the industry around tracking.

CNAME Cloaking Defense

ITP now caps the expiry of cookies set in so-called third-party CNAME-cloaked HTTP responses to 7 days. On macOS, this enhancement is specific to Big Sur.

What Is CNAME Cloaking?

In the eyes of web browsers, the first party of a website is typically defined by its registrable domain. This means that www.blog.example and comments.blog.example are considered same-site and the same party. If the user loads a webpage from www.blog.example, and that page makes a subresource request to comments.blog.example, that request will carry all cookies that are set to cover the blog.example site, including login cookies and user identity cookies. In addition, the response to that comments.blog.example subresource request can set cookies for blog.example, and those cookies will be first-party cookies.

Enter CNAMEs. CNAME stands for canonical name record and maps one domain name to another as part of the Domain Name System, or DNS. This means a site owner can configure one of their subdomains, such as sub.blog.example, to resolve to thirdParty.example, before resolving to an IP address. This happens underneath the web layer and is called CNAME cloaking — the thirdParty.example domain is cloaked as sub.blog.example and thus has the same powers as the true first party.

CNAME Cloaking and Tracking

Cross-site trackers have convinced site owners to set up CNAME cloaking in order to circumvent tracking prevention, such as ITP’s 7-day expiry cap on cookies set in JavaScript. In our blog case, this would be making track.blog.example resolve to tracker.example.

A recent paper from researchers at the Graduate University for Advanced Studies (Sokendai) and the French National Cybersecurity Agency (ANSSI) found 1,762 websites CNAME cloaking 56 trackers in total.

CNAME Cloaking and Website Security

Site owners who set up CNAME cloaking risk full website takeovers or customer cookie hijacking if the CNAME records aren’t properly managed, for instance if CNAME cloaking isn’t decommissioned when no longer in use. It was recently reported that 250 websites of banks, healthcare companies, restaurant chains, and civil rights groups had been compromised through mismanaged CNAME cloaking. In June this year, Microsoft documented these attacks and how their cloud customers should prevent them.

ITP’s Defense Against CNAME Cloaking Tracking

ITP now detects third-party CNAME cloaking requests and caps the expiry of any cookies set in the HTTP response to 7 days. This cap is aligned with ITP’s expiry cap on all cookies created through JavaScript.

Third-party CNAME cloaking is defined as a first-party subresource that resolves through a CNAME that differs from the first-party domain and differs from the top frame host’s CNAME, if one exists. Yes, the whole site can be CNAME cloaked, when it uses so called edge servers.

The best way to explain this is through a table (1p means first-party, 3p means third-party):

1p host, e.g. www.blog.example 1p subdomain other than the 1p host, e.g. track.blog.example Capped cookie expiry?
No cloaking No cloaking No cap
No cloaking other.blog.example (1p cloaking) No cap
No cloaking tracker.example (3p cloaking) 7-day cap
abc123.edge.example (cloaking) No cloaking No cap
abc123.edge.example (cloaking) abc123.edge.example (matching cloaking) No cap
abc123.edge.example (cloaking) other.blog.example (1p cloaking) No cap
abc123.edge.example (cloaking) tracker.example (3p cloaking) 7-day cap

SameSite=Strict Cookie Jail for Bounce Trackers

In June 2018, we announced an update to ITP to detect and defend against first party bounce trackers. In March 2020, we announced an enhancement to also detect delayed bounce tracking. Since then, we have received a report of one specific website engaged in bounce tracking while also being likely to get frequent user interaction. To combat such issues, we proposed to the W3C Privacy Community Group what we call a SameSite=Strict jail as well as other escalations.

What the SameSite=strict jail does is detect bounce tracking and, at a certain threshold, rewrite all the tracking domain’s cookies to SameSite=strict. This means that they will not be sent in cross-site, first-party navigations, and they can no longer be used for simple redirect-based bounce tracking.

Our implementation is rather relaxed, with the threshold set to 10 unique navigational, first-party redirects (unique in the sense of going to unique domains), and an automatic reset of that counter once the cookies are rewritten to SameSite=strict. This automatically gives the domain a new chance so that they can disengage in bounce tracking and “get out of jail.”

Our current list of domains we subject to this protection is empty because the domain reported to us has stopped their bounce tracking. But this protection remains in our toolbox.

Partitioned Ephemeral IndexedDB

Up until now, WebKit has blocked cross-origin IndexedDB. WebKit now allows partitioned and ephemeral third-party IndexedDB in an effort to align with other browsers now that they are interested in storage partitioning too. You can partake in the ongoing standardization effort for storage partitioning on GitHub.

Partitioned means unique IndexedDB instance per first-party site and ephemeral means in-memory-only, i.e. goes away on browser quit.

Third-Party Cookie Blocking and Storage Access API In Private Browsing

Private Browsing in Safari is based on WebKit’s ephemeral sessions where nothing is persisted to disk. This means ITP would not be able to learn things between launches of Safari. Further, Private Browsing also uses a separate ephemeral session for each new tab the user opens. To uphold this separation between tabs, ITP wouldn’t be able to classify cross-site trackers from the user’s full browsing even in-memory.

However, full third-party cookie blocking doesn’t need classification and is now enabled by default in Private Browsing. This might seem simple to support but the challenge was to make the Storage Access API work with the aforementioned tab separation. This is how it works: Say identityProvider.example wants to request storage access as third-party on the login page for social.example in Tab A. Interacting with identityProvider.example as a first party website in Tab B will not suffice to allow it to request storage access in Tab A since that would leak state between the separate ephemeral sessions. Thus, the user must interact with identityProvider.example in the same tab as where identityProvider.example later requests storage access as third-party. This makes sure that login flows where two different parties are involved and third-party cookie access is required, is possible in Private Browsing mode.

Home Screen Web Application Domain Exempt From ITP

Back in March 2020, when we announced ITP’s 7-day cap on all script-writeable storage, developers asked about home screen web applications and whether they were exempt from this 7-day cap. We explained how ITP’s counter of “days of use” and capture of user interaction effectively made sure that the first party of home screen web applications would not be subjected to the new 7-day cap. To make this more clear, we have implemented an explicit exception for the first-party domain of home screen web applications to make sure ITP always skips that domain in its website data removal algorithm.

In addition, the website data of home screen web applications is kept isolated from Safari and thus will not be affected by ITP’s classification of tracking behavior in Safari.

Thanks To My Coworkers

The above updates to WebKit and ITP would not have been possible without the help from Kate, Jiten, Scott, Tommy, Sihui, and David. Thank you!

]]>
App-Bound Domains https://webkit.org/blog/10882/app-bound-domains/ Fri, 26 Jun 2020 17:00:01 +0000 https://webkit.org/?p=10882 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:

  1. Use SafariViewController instead of WKWebView 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.
  2. 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.

]]>
Full Third-Party Cookie Blocking and More https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ Tue, 24 Mar 2020 17:59:55 +0000 https://webkit.org/?p=10218 This blog post covers several enhancements to Intelligent Tracking Prevention (ITP) in iOS and iPadOS 13.4 and Safari 13.1 on macOS to address our latest discoveries in the industry around tracking.

Full Third-Party Cookie Blocking

Cookies for cross-site resources are now blocked by default across the board. This is a significant improvement for privacy since it removes any sense of exceptions or “a little bit of cross-site tracking is allowed.”

It might seem like a bigger change than it is. But we’ve added so many restrictions to ITP since its initial release in 2017 that we are now at a place where most third-party cookies are already blocked in Safari. To keep supporting cross-site integration, we shipped the Storage Access API two years ago to provide the means for authenticated embeds to get cookie access with mandatory user control. It is going through the standards process in the W3C Privacy Community Group right now.

Regardless of the size of this change, there are further benefits, as explored below.

Paves the Way For Other Browsers

Safari continues to pave the way for privacy on the web, this time as the first mainstream browser to fully block third-party cookies by default. As far as we know, only the Tor Browser has featured full third-party cookie blocking by default before Safari, but Brave just has a few exceptions left in its blocking so in practice they are in the same good place. We know Chrome wants this behavior too and they announced that they’ll be shipping it by 2022.

We will report on our experiences of full third-party cookie blocking to the privacy groups in W3C to help other browsers take the leap.

Removes Statefulness From Cookie Blocking

Full third-party cookie blocking removes statefulness in cookie blocking. As discussed in our December 2019 blog post, the internal state of tracking prevention could be turned into a tracking vector. Full third-party cookie blocking makes sure there’s no ITP state that can be detected through cookie blocking behavior. We’d like to again thank Google for initiating this analysis through their report.

Disables Login Fingerprinting

As discussed by Jeremiah Grossman back in 2008 and Tom Anthony in 2012, and set up by Robin Linus in 2016 as a live demo with which you can test your browser, this technique allows a website to invisibly detect where you are logged in and is viable in any browser without full third-party cookie blocking.

Since “global browser state” has been top of mind in the web privacy community as of late, we’d like to point out that cookies themselves are global state and unless the browser blocks or partitions them in third-party contexts, they allow for cross-site leakage of user information such as login fingerprinting.

Additional Benefits

In addition, there are further benefits to full third-party cookie blocking:

  • Disables cross-site request forgery attacks against websites through third-party requests. Note that you still need to protect against forged requests that come in through top frame navigations (see SameSite cookies for guidance).
  • Removes the ability to use an auxiliary third-party domain to identify users. Such a setup could otherwise persist IDs even when users delete website data for the first party.
  • Simplifies things for developers. Now it’s as easy as possible: If you need cookie access as third-party, use the Storage Access API.

What About the Classifier?

ITP’s classifier keeps working to detect bounce trackers, tracker collusion, and link decoration tracking.

Developer Guidance

If yours is among the few websites that still relies on third-party cookies in Safari and has not been affected by ITP in its previous iterations, here’s how you can make things work for your users:

Option 1: OAuth 2.0 Authorization with which the authenticating domain (in your case, the third-party that expects cookies) forwards an authorization token to your website which you consume and use to establish a first-party login session with a server-set Secure and HttpOnly cookie.

Option 2: The Storage Access API with which the third-party can request permission to get access to its first-party cookies.

Option 3: The temporary compatibility fix for popups, see section “Temporary Compatibility Fix: Automatic Storage Access for Popups” in our ITP 2.0 blog post. This compatibility fix allows the third-party to open a popup from your website and upon a tap or click in that popup gain temporary cookie access under the opener page on your website. Note that this compatibility fix will go away in a future version of Safari so only go this route if it saves you time and allows for a graceful transition period.

Cookie Blocking Latch Mode

The original release of ITP featured what we call “cookie blocking latch mode.” It means once a request is blocked from using cookies, all redirects of that request are also blocked from using cookies. Back in 2017 we got a request to allow cookie blocking to open and close on redirects and implemented that behavior. But with full third-party cookie blocking in place, latch mode is back.

7-Day Cap on All Script-Writeable Storage

Back in February 2019, we announced that ITP would cap the expiry of client-side cookies to seven days. That change curbed third-party scripts’ use of first-party cookies for the purposes of cross-site tracking.

However, as many anticipated, third-party scripts moved to other means of first-party storage such as LocalStorage. If you have a look at what’s stored in the first-party space on many websites today, it’s littered with data keyed as various forms of “tracker brand user ID.” To make matters worse, APIs like LocalStorage have no expiry function at all, i.e. websites cannot even ask browsers to put a limit on how long such storage should stay around.

Now ITP has aligned the remaining script-writable storage forms with the existing client-side cookie restriction, deleting all of a website’s script-writable storage after seven days of Safari use without user interaction on the site. These are the script-writable storage forms affected (excluding some legacy website data types):

  • Indexed DB
  • LocalStorage
  • Media keys
  • SessionStorage
  • Service Worker registrations and cache

A Note On Web Applications Added to the Home Screen

As mentioned, the seven-day cap on script-writable storage is gated on “after seven days of Safari use without user interaction on the site.” That is the case in Safari. Web applications added to the home screen are not part of Safari and thus have their own counter of days of use. Their days of use will match actual use of the web application which resets the timer. We do not expect the first-party in such a web application to have its website data deleted.

If your web application does experience website data deletion, please let us know since we would consider it a serious bug. It is not the intention of Intelligent Tracking Prevention to delete website data for first parties in web applications.

Cross-Site document.referrer Downgraded to Origin

All cross-site document.referrers are downgraded to their origin. This matches the already downgraded cross-site referrer request headers.

Detection of Delayed Bounce Tracking

Some trackers have started to delay their navigational redirects, probably to evade ITP’s bounce tracking detection. This manifests as the webpage disappearing and reloading shortly after you land on it. We’ve added logic to cover such delayed bounce tracking and detect them just like instant bounces.

Testing Your Website

We encourage all developers to regularly test their websites with Safari Technology Preview (STP) and our betas of iOS, iPadOS, and macOS. Major changes to ITP and WebKit in general are included in the betas and STP, typically months before shipping. An easy way to stay ahead of the changes is to use STP as a daily development browser. This gives you access to the latest developer tools and helps you discover unexpected behavior in your websites with each release. If you come across bugs or breakage, please file an open source bug report.

]]>
Preventing Tracking Prevention Tracking https://webkit.org/blog/9661/preventing-tracking-prevention-tracking/ Tue, 10 Dec 2019 18:30:48 +0000 https://webkit.org/?p=9661 This blog post covers enhancements to Intelligent Tracking Prevention (ITP) included in Safari on iOS and iPadOS 13.3, Safari 13.0.4 on macOS Catalina, Mojave, and High Sierra.

Tracking Prevention as a Tracking Vector

Any kind of tracking prevention or content blocking that treats web content differently based on its origin or URL risks being abused itself for tracking purposes if the set of origins or URLs provide some uniqueness to the browser and webpages can detect the differing treatment.

To combat this, tracking prevention features must make it hard or impossible to detect which web content and website data is treated as capable of tracking. We have devised three ITP enhancements that not only fight detection of differing treatment but also improve tracking prevention in general.

Origin-Only Referrer For All Third-Party Requests

ITP now downgrades all cross-site request referrer headers to just the page’s origin. Previously, this was only done for cross-site requests to classified domains.

As an example, a request to https://images.example that would previously contain the referrer header “https://store.example/baby/strollers/deluxe-stroller-navy-blue.html” will now be reduced to just “https://store.example/”.

All Third-Party Cookies Blocked on Websites Without Prior User Interaction

ITP will now block all third-party requests from seeing their cookies, regardless of the classification status of the third-party domain, unless the first-party website has already received user interaction.

The Storage Access API Takes the Underlying Cookie Policy Into Consideration

Safari’s original cookie policy restricts third-parties from setting cookies unless they already have set cookies as first-party. It is still in effect underneath ITP.

As of this ITP update, the Storage Access API takes Safari’s cookie policy into consideration when handling calls to document.hasStorageAccess().

Now a call to document.hasStorageAccess() may resolve with false for one of two reasons:

  1. Because ITP is blocking cookies and explicit storage access has not been granted.
  2. Because the domain doesn’t have cookies and thus the cookie policy is blocking cookies.

Developers have asked for this change because previously document.hasStorageAccess() could resolve with true but the iframe still couldn’t set cookies because of the cookie policy. This is a rare case in practice because Safari’s cookie policy has been in effect for a decade and a half and almost all websites that want to use cookies as third-party will also set cookies as first-party.

Absence of Cookies In Third-Party Requests Does Not Reveal ITP Status

A reasonable question to ask is can an attacker reveal ITP status for a domain outside their control by making a third-party request to the domain and checking for side effects of whether cookies were sent or not?

Safari’s default cookie policy requires a third-party to have “seeded” its cookie jar as first-party before it can use cookies as third-party. This means the absence of cookies in a third-party request can be due to ITP blocking existing cookies or the default cookie policy blocking cookies because the user never visited the website, the website’s cookies have expired, or because the user or ITP has explicitly deleted the website’s cookies.

Thus, the absence of cookies in a third-party request outside the attacker’s control is not proof that the third-party domain is classified by ITP.

Thanks To Google

We’d like to thank Google for sending us a report in which they explore both the ability to detect when web content is treated differently by tracking prevention and the bad things that are possible with such detection. Their responsible disclosure practice allowed us to design and test the changes detailed above. Full credit will be given in upcoming security release notes.

]]>