Skip to content

Commit

Permalink
Merge pull request #178 from miketaylr/issues/146/1
Browse files Browse the repository at this point in the history
Issue #146 - Specify GREASE algorithms (aka arbitrary brand and version number)
  • Loading branch information
miketaylr authored Jan 8, 2021
2 parents 5bca6e8 + f9e8cf5 commit e3b93ea
Showing 1 changed file with 63 additions and 28 deletions.
91 changes: 63 additions & 28 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -411,49 +411,89 @@ Note: The high-entropy portions of the user agent information are retrieved thro
Processing model {#processing}
--------------

<h3 id="monkeypatch-html-windoworworkerglobalscope"><code>WindowOrWorkerGlobalScope</code></h3>
<h4 id="monkeypatch-html-windoworworkerglobalscope"><code>WindowOrWorkerGlobalScope</code></h4>

Each [=user agent=] has an associated <dfn for="user agent">brands</dfn>, which is a [=/list=] created by running [=create brands=].
Each [=user agent=] has an associated <dfn for="user agent">brands</dfn>, which is a [=/list=] created by running
[=create brands=].

Every {{WindowOrWorkerGlobalScope}} object has an associated <dfn for="WindowOrWorkerGlobalScope">brands frozen array</dfn>, which is a <code><a interface>FrozenArray</a>&lt;<a dictionary>NavigatorUABrandVersion</a>></code>. It is initially the result of [=create a frozen array|creating a frozen array=] from the [=user agent=]'s [=brands=].
Every {{WindowOrWorkerGlobalScope}} object has an associated <dfn for="WindowOrWorkerGlobalScope">brands frozen array</dfn>,
which is a <code><a interface>FrozenArray</a>&lt;<a dictionary>NavigatorUABrandVersion</a>></code>. It is initially the
result of [=create a frozen array|creating a frozen array=] from the [=user agent=]'s [=brands=].

<h3 id="create-ua-list-section">Create brands</h3>
<h4 id="create-ua-list-section">Create brands</h4>

When asked to run the <dfn>create brands</dfn> algorithm, the [=user agent=] MUST run the following steps:
1. Let |list| be a [=/list=].

2. Collect pairs of [=user agent/brand=] and [=user agent/significant version=] which represent the [=user agent=],
its equivalence class and/or its rendering engine.
2. Collect pairs of [=user agent/brand=] and [=user agent/significant version=] which represent the [=user agent=] or
[=equivalence classes=].

3. For each pair:

1. Let |dict| be a new {{NavigatorUABrandVersion}} dictionary,
with [=user agent/brand=] as {{NavigatorUABrandVersion/brand}} and [=user agent/significant version=] as {{NavigatorUABrandVersion/version}}.
with [=user agent/brand=] as {{NavigatorUABrandVersion/brand}} and [=user agent/significant version=] as
{{NavigatorUABrandVersion/version}}.

2. Append |dict| to |list|.

4. The [=user agent=] SHOULD execute the following steps:

1. [=list/Append=] additional items to |list| containing {{NavigatorUABrandVersion}} objects,
initialized with arbitrary {{NavigatorUABrandVersion/brand}} and {{NavigatorUABrandVersion/version}} combinations.
1. [=list/Append=] one additional [=list/item=] to |list| containing a {{NavigatorUABrandVersion}} dictionary,
initialized with <a lt="create an arbitrary brand">arbitrary brand</a> and <a lt="create an arbitrary version">
arbitrary version</a> combinations.

2. Randomize the order of the items in |list|.
2. Randomize the order of the [=list/items=] in |list|.

Note: One approach to minimize caching variance when generating these random components could be to
determine them at build time, and keep them identical throughout the lifetime of the [=user agent=]'s significant
version.
version.

Note: See [[#grease]] for more details on when and why these randomization steps might be appropriate.

5. Return |list|.

<h3 id="getters">Getters</h3>
An <dfn for="user agent" export>equivalence class</dfn> represents a group of browsers believed to be compatibile with
each other. A shared rendering engine may form an [=equivalence class=], for example.

<h4 id="create-arbitrary-brands-section"
algorithm="to create arbitrary brand and version values">Create arbitrary brand and version values</h4>

To <dfn>create an arbitrary brand</dfn>, the [=user agent=] MUST run these steps:

1. Let |arbitraryBrand| be a [=/string=] composed of [=ASCII alpha=]. |arbitraryBrand| MUST contain one or more
0x20 (SP) bytes and be no longer than twenty [=ASCII bytes=].
1. Let |arbitraryBrandList| be the result of <a lt="split on ASCII whitespace">splitting |arbitraryBrand| on ASCII
whitespace</a>.
1. Let |greaseyStack| be a [=stack=].
1. Let |greaseyChars| be the [=list=] of [=ASCII bytes=] « 0x20 (SP), 0x28 (left parenthesis), 0x29
(right parenthesis), 0x2D (-), 0x2E (.), 0x2F (/), 0x3A (:), 0x3B (;), 0x3D (=), 0x3F (?), 0x5F (_) ».
1. For each item of |arbitraryBrandList|, [=stack/push=] a randomly selected [=list/item=] from |greaseyChars| onto
|greaseyStack|.
1. Let |greaseyBrandList| be a [=list=] and |index| be 0.
1. While |greaseyStack| [=stack/is not empty=]:
1. Let |item| be the result of <a lt="pop">popping</a> from |greaseyStack|.
1. Append |item| to |greaseyBrandList|.
1. Append |arbitraryBrandList|[|index|] to |greaseyBrandList|.
1. Increment |index| by 1.
1. Return the result of <a lt="strip leading and trailing ASCII whitespace"> stripping leading and trailing ASCII
whitespace</a> from the [=concatenation=] of |greaseyBrandList| (with no separator).

Note: [=Structured Headers=] allows for escaped 0x22 (\") and 0x5C (\\) inside a [=structured header/string=], but
these are known to not be web-compatible.

To <dfn>create an arbitrary version</dfn>, return a [=/string=] that MUST match the format of the [=user agent=]'s
[=user agent/significant version=], but MUST NOT match the value.

Note: User Agents may decide to send arbitrarily low versions to ensure proper version checking, and should vary them
over time.

<h4 id="getters">Getters</h4>

On getting, the {{NavigatorUAData/brands}} attribute MUST return [=this=]'s [=relevant global object=]'s [=WindowOrWorkerGlobalScope/brands frozen array=].

On getting, the {{NavigatorUAData/mobile}} attribute must return the [=user agent=]'s [=user agent/mobileness=].

<h3 id="getHighEntropyValues"><code>getHighEntropyValues</code> method</h3>
<h4 id="getHighEntropyValues"><code>getHighEntropyValues</code> method</h4>

The <dfn method for="NavigatorUA"><code>getHighEntropyValues(|hints|)</code></dfn> method MUST run these steps:

Expand Down Expand Up @@ -537,48 +577,43 @@ One approach which might be advisable could be for each [=user agent=] to lock t
the version number, then shifting platform and model information to something reasonably generic in
order to reduce the fingerprint the header provides.

GREASE-like UA Strings {#grease}
GREASE-like UA Brand Lists {#grease}
----------------------

History has shown us that there are real incentives for [=user agents=] to lie about their branding
in order to thread the needle of sites' sniffing scripts, and prevent their users from being blocked
by UA-based allow/block lists.

Resetting expectations may help to prevent abuse of the UA string's brand in the short term, but
probably won't help in the long run. The world of network protocols introduced the notion of <abbr
Resetting expectations may help to prevent abuse of the [=user agent/brands=] list in the short term, but
probably won't help in the long run. The world of network protocols introduced the notion of <abbr
title="Generate Random Extensions And Sustain Extensibility">GREASE</abbr> [[I-D.ietf-tls-grease]].
We could borrow from that concept to tackle this problem.

[=User agents=]' [=user agent/brands=] containing more than a single entry could encourage
standardized processing of the `UA` string. By randomly including additional, intentionally
standardized processing of the [=user agent/brands=] list. By randomly including additional, intentionally
incorrect, comma-separated entries with arbitrary ordering, they would reduce the chance that we
ossify on a few required strings.

Let's examine a few examples:
* In order to avoid sites from barring unknown browsers from their allow lists, Chrome could send a
UA set that includes an non-existent browser, and which varies once in a while.
- `"Chrome"; v="73", "NotBrowser"; v="12"`
* In order to enable equivalence classes based on Chromium versions, Chrome could add the rendering
* In order to enable [=equivalence classes=] based on Chromium versions, Chrome could add the rendering
engine and its version to that.
- `"Chrome"; v="73", "NotBrowser"; v="12", "Chromium"; v="73"`
* In order to encourage sites to rely on equivalence classes based on Chromium versions rather than
* In order to encourage sites to rely on [=equivalence classes=] based on Chromium versions rather than
exact UA sniffing, Chrome might remove itself from the set entirely.
- `"Chromium"; v="73", "NotBrowser"; v="12"`
* Browsers based on Chromium may use a similar UA string, but use their own brand as part of the
set, enabling sites to count them.
- `"Chrome"; v="73", "Awesome Browser"; v="60", "Chromium"; v="73"`


[=User agents=] MUST include more than a single value in [=user agent/brands=], where at least one
of these values is an arbitrary value.

When adding arbitrary values to [=user agent/brands=], [=user agents=] MUST make sure that receivers
of the header adhere to [=Structured Header=] parsing, by adding escaped double-quotes, commas and
semi-colons to those values. The purpose of this is to make non-compliant server implementations
immediately aware that their parsing code is inadequate.
[=User agents=] MUST include more than a single value in [=user agent/brands=], where one of these values is an
arbitrary value.

The value order in [=user agent/brands=] MUST change over time, the prevent receivers of the header
from relying on certain values being in certain locations in the string.
The value order in [=user agent/brands=] MUST change over time to prevent receivers of the header
from relying on certain values being in certain locations in the list.

When choosing GREASE strategies, [=user agents=] SHOULD keep caching variance in mind and minimize
variance among identical [=user agent=] versions.
Expand Down

0 comments on commit e3b93ea

Please sign in to comment.