1 من 34

User-Agent evolution

String to Client Hints

merewood@ / @rowan_m

Confidential + Proprietary

Confidential + Proprietary

2 من 34

What are we going to talk about?

  1. User-Agent string → passive fingerprinting surface
  2. Chrome → reducing the information in its user-agent
  3. User-Agent Client Hints → active API for the same data
  4. How do you migrate from string to client hints?

Confidential + Proprietary

3 من 34

User-Agent string

  • Original RFC 1945 example from 1996:

User-Agent: CERN-LineMode/2.15 libwww/2.17b3

Confidential + Proprietary

4 من 34

User-Agent string

  • Original RFC 1945 example from 1996:

User-Agent: CERN-LineMode/2.15 libwww/2.17b3

  • My phone example from today:

Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.61 Mobile Safari/537.36

Confidential + Proprietary

5 من 34

User-Agent String

  • Passive fingerprinting surface
  • Complicated, inconsistent format

Confidential + Proprietary

6 من 34

60-70%

"…of HTTP user-agent strings can accurately identify hosts in our datasets"

Microsoft research: Host Fingerprinting and Tracking on the Web:Privacy and Security Implications

Confidential + Proprietary

7 من 34

Reducing Chrome's�user-agent string

Confidential + Proprietary

Confidential + Proprietary

8 من 34

Reducing Chrome's user-agent string

  • Current user-agent string format

Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36

  • Final reduced user-agent string format

Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36

Full phases detailed at: chromium.org/updates/ua-reduction

Confidential + Proprietary

9 من 34

Early opt-in origin trial

Confidential + Proprietary

Confidential + Proprietary

10 من 34

Privacy Sandbox patterns

  • Passive fingerprinting
    • Active requests
  • Raw data
    • Specific answers

Confidential + Proprietary

Confidential + Proprietary

11 من 34

Client Hints

⬆️ Client (browser) sends initial request

⬇️ Server specifies desired hints in response

⬆️ Client may send hints in subsequent requests

Confidential + Proprietary

12 من 34

⬇️ Server response� Accept-CH: Viewport-Width, Width

↪️ Or a meta tag<meta http-equiv="Accept-CH" content="Viewport-Width, Width" />

⬆️ Client request� Viewport-Width: 460� Width: 230

Confidential + Proprietary

13 من 34

Migrate User-Agent data to Client Hints for a�cleaner, active method of obtaining the same information.

  • User-Agent string
    • User-Agent Client Hints

Confidential + Proprietary

Confidential + Proprietary

14 من 34

User-Agent Client Hints

⬆️ Initial client request� Sec-CH-UA: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"� Sec-CH-UA-Mobile: ?0� Sec-CH-UA-Platform: "Linux"

⬇️ Server response� Accept-CH: Sec-CH-UA-Full-Version

⬆️ Client request� Sec-CH-UA: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"� Sec-CH-UA-Mobile: ?0� Sec-CH-UA-Platform: "Linux"� Sec-CH-UA-Full-Version: "91.0.4469.4"

Confidential + Proprietary

15 من 34

Available hints - Sec-CH-UA-*

  • [UA]: Default list of brand + significant version, e.g. "Google Chrome";v="91"
  • Arch: CPU architecture, e.g. "x86"
  • Bitness: CPU architecture bitness, e.g. "64"
  • Full-Version: Build version, e.g. "91.0.4469.4"
  • Mobile: Boolean indicating a mobile device, e.g. ?0 or ?1
  • Model: Device model, e.g. "Pixel 5"
  • Platform: Operating system, e.g. "Android"
  • Platform-Version: Operating system version, e.g. "11"

Confidential + Proprietary

16 من 34

Confidential + Proprietary

17 من 34

Accept-CH cache

The browser will continue to send the hints requested by the last Accept-CH header until:

  • the session ends
  • storage is cleared
  • another Accept-CH header is received.

Confidential + Proprietary

18 من 34

Client Hints on cross-origin requests

⬇️ Response from example.com� Accept-CH: Sec-CH-UA-Model, DPR� Permissions-Policy: ch-ua-model=(self "https://downloads.example.com"),� ch-dpr (self "img.example.com")

⬆️ Request to downloads.example.com� Sec-CH-UA-Model: "Pixel 5"

⬆️ Requests to img.example.com� DPR: 2

Confidential + Proprietary

19 من 34

Confidential + Proprietary

20 من 34

What if I need all the user-agent data on first load?

Confidential + Proprietary

21 من 34

What if I need all the user-agent data on first load?

😽

Confidential + Proprietary

22 من 34

Things that are not the first request

  • Same-origin navigations
  • JavaScript
  • Content in iframes
  • Images
  • Any subresource
  • Literally anything that is not the very first inbound contact from the browser in that session.

Confidential + Proprietary

23 من 34

If you really need specific user-agent data on first load

⬆️ Initial client request� Sec-CH-UA: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"� Sec-CH-UA-Mobile: ?1� Sec-CH-UA-Platform: "Android"

⬇️ Server response� Accept-CH: Sec-CH-UA-Platform-Version� Critical-CH: Sec-CH-UA-Platform-Version

⬆️ Client retries same request� Sec-CH-UA: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"� Sec-CH-UA-Mobile: ?1� Sec-CH-UA-Platform: "Android"� Sec-CH-UA-Platform-Version: "11"

Confidential + Proprietary

24 من 34

Confidential + Proprietary

25 من 34

Avoiding the round trip with Application-Layer Protocol Settings

ClientHello� + alps

ServerHello�EncryptedExtensions�+ alps=(https://example.com, Sec-CH-UA-Platform-Version)�…�Finished

Finished� GET / HTTP/2.0� Host: example.com� Sec-CH-UA-Platform-Version: 11

HTTP/2.0 200 OK�Vary: Sec-CH-UA-Platform-Version�Accept-CH: Sec-CH-UA-Platform-Version�Critical-CH: Sec-CH-UA-Platform-Version

Confidential + Proprietary

26 من 34

  • Migration strategies�web.dev/migrate-to-ua-ch/

Confidential + Proprietary

Confidential + Proprietary

27 من 34

Confidential + Proprietary

28 من 34

Literally do nothing option

  1. Audit use of User-Agent string in back-end and front-end
  2. If only the base values of brand, major version, and platform are used then these will probably be available in the frozen UA string

Confidential + Proprietary

29 من 34

Front-end option

  1. Check code for usage of navigator.userAgent
    1. Also the deprecated navigator.platform or navigator.appVersion
  2. Wrap with appropriate calls to detect:

if (navigator.userAgentData) {

// use new hints

} else {

// fall back to user-agent string parsing

}

Confidential + Proprietary

30 من 34

Server-side static header option

  1. Audit use of User-Agent string
  2. Set headers for required hints across the whole site
    1. Or no headers if the defaults are enough
  3. Switch from checking User-Agent header to relevant Sec-CH-UA headers

Confidential + Proprietary

31 من 34

Hints needed on first request option

  1. Audit use of user-agent data
    1. Are the non-default values really needed on first request? What's the impact of them not being there?
  2. Can that data be requested client-side? Use navigator.userAgentData
  3. Is that first request to a sub-resource? Use Feature Policy / Permission Policy
  4. Really needed on first request? Use Critical-CH

Confidential + Proprietary

32 من 34

Dynamic requirements option

  1. Audit use of user-agent data
  2. Are there specific user journeys that require richer data?
  3. Can these be handled client-side with navigator.userAgentData?
  4. Can headers be configured per route?

Confidential + Proprietary

33 من 34

Using third-party libraries option

  1. Do you use a device detection or similar user-agent parsing library?
  2. Add a feature request for them to support User-Agent Client Hints
    1. TODO: reasonable template for these as we want to avoid… (see next slide)

Confidential + Proprietary

34 من 34

Retrofill option - please don't

  • Audit use of user-agent data
  • Is there legacy / unknown code using user-agent data?
  • Request all Client Hints and rebuild the user-agent string server-side
  • Request all data from navigator.userAgentData and overwrite navigator.userAgent

Confidential + Proprietary