User-Agent Client Hints API によるユーザーのプライバシーとデベロッパー エクスペリエンスの向上

User-Agent Client Hints は Client Hints API の新しい拡張であり、デベロッパーはプライバシーを保護しながら人間工学に基づいた方法で、ユーザーのブラウザに関する情報にアクセスできます。

Client Hints を使用すると、デベロッパーは、User-Agent(UA)文字列から解析しなくても、ユーザーのデバイスや状態に関する情報を積極的にリクエストできます。この代替ルートを提供することが、最終的にユーザー エージェント文字列の粒度を下げるための第一歩です。

ユーザー エージェント文字列の解析に依存する既存の機能を更新し、代わりに User-Agent Client Hints を使用する方法について説明します。

背景

ウェブブラウザがリクエストを行うと、ブラウザとその環境に関する情報が含まれます。これにより、サーバーは分析を有効にしてレスポンスをカスタマイズできます。これは 1996 年(HTTP/1.0 の RFC 1945)にまでさかのぼって定義されました。User-Agent 文字列の元の定義で、次のような例を確認できます。

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

このヘッダーは、プロダクト(ブラウザやライブラリなど)とコメント(バージョンなど)を重要度の高い順に指定するためのものです。

ユーザー エージェント文字列の状態

それから何十年もの間、この文字列は、リクエストを行っているクライアントに関するさまざまな追加の詳細を生み出してきました(後方互換性があるため、処理が困難になります)。Chrome の現在のユーザー エージェント文字列を見ると、次のことがわかります。

Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4076.0 Mobile Safari/537.36

上記の文字列には、ユーザーのオペレーティング システムとバージョン、デバイスモデル、ブラウザのブランドとフルバージョン、モバイル ブラウザであると推測するための十分な手がかりが含まれています。また、歴史的な理由から他のブラウザへの参照が多数含まれていることは言うまでもありません。

これらのパラメータと取り得る値が多様であるため、ユーザー エージェント文字列には、個々のユーザーを一意に識別するのに十分な情報が含まれている可能性があります。AmIUnique でブラウザをテストすると、ユーザー エージェント文字列がユーザーを識別できる精度がどの程度近いかを確認できます。結果の「類似度」が低いほど、リクエストの独自性が高いほど、サーバーがユーザーを密かにトラッキングしやすくなります。

ユーザー エージェント文字列は、多くの正当なユースケースを可能にし、デベロッパーとサイト所有者にとって重要な目的を果たします。ただし、隠れたトラッキング手法からユーザーのプライバシーを保護することも重要であり、デフォルトで UA 情報を送信することはその目標に反します。

また、ユーザー エージェント文字列に関しては、ウェブの互換性を向上させる必要があります。構造化されていないため、解析すると不必要に複雑になり、多くの場合、ユーザーに悪影響を及ぼすバグやサイトの互換性の問題を引き起こします。また、サイトが構成に対するテストに失敗した可能性があるため、あまり一般的でないブラウザを使用しているユーザーは不釣り合いなほど悪影響を及ぼします。

新しい User-Agent Client Hints の概要

User-Agent Client Hints を使用すると、同じ情報にアクセスできますが、プライバシーに配慮した方法で処理されます。これにより、ブラウザは最終的に、すべてをブロードキャストする User-Agent 文字列のデフォルトを減らすことができます。Client Hints はモデルを適用します。サーバーは、クライアントに関する一連のデータ(ヒント)をブラウザに要求し、ブラウザは独自のポリシーまたはユーザー構成を適用して、返されるデータを決定します。つまり、デフォルトでユーザー エージェント情報をすべて公開するのではなく、アクセス権が明示的かつ監査可能な方法で管理されるようになりました。また、正規表現が不要というシンプルな API のメリットを享受することもできます。

現在の Client Hints セットは、主にブラウザの表示機能と接続機能について説明しています。詳細については、Client Hints によるリソース選択の自動化で説明していますが、ここではそのプロセスについて簡単に復習します。

サーバーはヘッダーを介して特定の Client Hints を要求します。

⬇️ サーバーからのレスポンス

Accept-CH: Viewport-Width, Width

メタタグの場合:

<meta http-equiv="Accept-CH" content="Viewport-Width, Width" />

これにより、ブラウザは後続のリクエストで次のヘッダーを返すことができます。

🙁?️ 後続のリクエスト

Viewport-Width: 460
Width: 230

サーバーでは、適切な解像度で画像を提供するなど、さまざまなレスポンスを選択できます。

User-Agent Client Hints は、Accept-CH サーバー レスポンス ヘッダーを介して指定できる Sec-CH-UA 接頭辞を持つプロパティの範囲を拡張します。まず説明から始めて、提案書全体に進みます。

Chromium 89 の User-Agent Client Hints

Chrome のバージョン 89 以降、User-Agent Client Hints がデフォルトで有効になっています。

デフォルトでは、ブラウザはブラウザのブランド、重要なバージョン / メジャー バージョン、プラットフォーム、クライアントがモバイル デバイスかどうかを示すインジケーターを返します。

⬆️ すべてのリクエスト

Sec-CH-UA: "Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "macOS"

ユーザー エージェントのレスポンスとリクエスト ヘッダー

⬇️ レスポンス Accept-CH
Ъ️ リクエスト ヘッダー
⬆️ リクエスト
値の例
説明
Sec-CH-UA "Chromium";v="84",
"Google Chrome";v="84"
ブラウザのブランドとその重要なバージョンのリスト。
Sec-CH-UA-Mobile ?1 ブラウザがモバイル デバイス上にあるか(true の場合は ?1)、そうでない(false の場合は ?0)かを示すブール値。
Sec-CH-UA-Full-Version "84.0.4143.2" [非推奨]ブラウザの完全版。
Sec-CH-UA-Full-Version-List "Chromium";v="84.0.4143.2",
"Google Chrome";v="84.0.4143.2"
ブラウザ ブランドとその完全版の一覧。
Sec-CH-UA-Platform "Android" デバイスのプラットフォーム、通常はオペレーティング システム(OS)です。
Sec-CH-UA-Platform-Version "10" プラットフォームまたは OS のバージョン。
Sec-CH-UA-Arch "arm" デバイスの基盤となるアーキテクチャ。ページの表示とは関係ないかもしれませんが、サイトでは、デフォルトで適切な形式に設定されたダウンロードの提供が求められている場合があります。
Sec-CH-UA-Model "Pixel 3" デバイスのモデル。
Sec-CH-UA-Bitness "64" 基盤となるアーキテクチャのビット数(整数またはメモリアドレスのビット数)

交換の例

交換の例を次に示します。

⬆️ ブラウザからの最初のリクエスト
ブラウザはサイトから /downloads ページをリクエストし、デフォルトの基本的な User-Agent を送信します。

GET /downloads HTTP/1.1
Host: example.site

Sec-CH-UA: "Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"
Sec-CH-UA-Mobile: ?1
Sec-CH-UA-Platform: "Android"

⬇️ サーバーからのレスポンス
サーバーはページを送り返し、さらにブラウザの完全版とプラットフォームを求めます。

HTTP/1.1 200 OK
Accept-CH: Sec-CH-UA-Full-Version-List

🙁?️ 後続のリクエスト
ブラウザはサーバーに追加情報へのアクセスを許可し、後続のすべてのリクエストで追加のヒントを返します。

GET /downloads/app1 HTTP/1.1
Host: example.site

Sec-CH-UA: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
Sec-CH-UA-Mobile: ?1
Sec-CH-UA-Full-Version-List: " Not A;Brand";v="99.0.0.0", "Chromium";v="98.0.4738.0", "Google Chrome";v="98.0.4738.0"
Sec-CH-UA-Platform: "Android"

JavaScript API

ヘッダーに加えて、JavaScript で navigator.userAgentData を使用して User-Agent にアクセスすることもできます。デフォルトの Sec-CH-UASec-CH-UA-MobileSec-CH-UA-Platform ヘッダー情報には、それぞれ brands プロパティと mobile プロパティを介してアクセスできます。

// Log the brand data
console.log(navigator.userAgentData.brands);

// output
[
  {
    brand: 'Chromium',
    version: '93',
  },
  {
    brand: 'Google Chrome',
    version: '93',
  },
  {
    brand: ' Not;A Brand',
    version: '99',
  },
];

// Log the mobile indicator
console.log(navigator.userAgentData.mobile);

// output
false;

// Log the platform value
console.log(navigator.userAgentData.platform);

// output
"macOS";

追加の値には、getHighEntropyValues() 呼び出しを介してアクセスします。「高エントロピー」という用語は、情報エントロピーのことを指します。言い換えれば、これらの値がユーザーのブラウザに関して公開する情報の量のことです。追加のヘッダーをリクエストする場合と同様に、値がある場合にどの値が返されるかはブラウザによって異なります。

// Log the full user-agent data
navigator
  .userAgentData.getHighEntropyValues(
    ["architecture", "model", "bitness", "platformVersion",
     "fullVersionList"])
  .then(ua => { console.log(ua) });

// output
{
   "architecture":"x86",
   "bitness":"64",
   "brands":[
      {
         "brand":" Not A;Brand",
         "version":"99"
      },
      {
         "brand":"Chromium",
         "version":"98"
      },
      {
         "brand":"Google Chrome",
         "version":"98"
      }
   ],
   "fullVersionList":[
      {
         "brand":" Not A;Brand",
         "version":"99.0.0.0"
      },
      {
         "brand":"Chromium",
         "version":"98.0.4738.0"
      },
      {
         "brand":"Google Chrome",
         "version":"98.0.4738.0"
      }
   ],
   "mobile":false,
   "model":"",
   "platformVersion":"12.0.1"
}

デモ

user-agent-client-hints.glitch.me から、ご自身のデバイスでヘッダーと JavaScript API の両方を試すことができます。

ヒントの有効期間とリセット

Accept-CH ヘッダーで指定されたヒントは、ブラウザ セッションの間、または別のヒントセットが指定されるまで送信されます。

つまり、サーバーが以下を送信するとします。

⬇️ 回答

Accept-CH: Sec-CH-UA-Full-Version-List

そうすると、ブラウザはブラウザを閉じるまで、そのサイトに対するすべてのリクエストで Sec-CH-UA-Full-Version-List ヘッダーを送信します。

⬆️ 後続のリクエスト

Sec-CH-UA-Full-Version-List: " Not A;Brand";v="99.0.0.0", "Chromium";v="98.0.4738.0", "Google Chrome";v="98.0.4738.0"

ただし、別の Accept-CH ヘッダーを受け取った場合、ブラウザが送信している現在のヒントは完全に置き換えられます

⬇️ 回答

Accept-CH: Sec-CH-UA-Bitness

⬆️ 後続のリクエスト

Sec-CH-UA-Platform: "64"

以前にリクエストされた Sec-CH-UA-Full-Version-List送信されません

Accept-CH ヘッダーは、そのページに必要なヒントの完全なセットを指定するものであると考えることをおすすめします。つまり、ブラウザはそのページのすべてのサブリソースについて、指定されたヒントを送信します。ヒントは次のナビゲーションまで保持されますが、サイトではヒントが配信されることを前提としたり、ヒントが提供されることを想定したりしないでください。

また、レスポンスで空の Accept-CH を送信することで、ブラウザから送信されたすべてのヒントを効果的に消去することもできます。ユーザーが設定をリセットしているときや、サイトからログアウトするときに、このセクションを追加することを検討してください。

このパターンは、<meta http-equiv="Accept-CH" …> タグを介したヒントの動作とも一致します。リクエストされたヒントは、ページによって開始されたリクエストでのみ送信され、後続のナビゲーションでは送信されません。

ヒントのスコープとクロスオリジン リクエスト

デフォルトでは、Client Hints は同一オリジンのリクエストでのみ送信されます。つまり、https://example.com で特定のヒントを要求しても、最適化するリソースが https://downloads.example.com にあると、ヒントが届きません

クロスオリジン リクエストでヒントを許可するには、各ヒントとオリジンを Permissions-Policy ヘッダーで指定する必要があります。これを User-Agent Client Hints に適用するには、ヒントを小文字にして、sec- 接頭辞を削除する必要があります。次に例を示します。

⬇️ example.com からの回答

Accept-CH: Sec-CH-UA-Platform-Version, DPR
Permissions-Policy: ch-ua-platform-version=(self "downloads.example.com"),
                    ch-dpr=(self "cdn.provider" "img.example.com");

⬆️ downloads.example.com へのリクエスト

Sec-CH-UA-Platform-Version: "10"

🙁?️ cdn.provider または img.example.com へのリクエスト

DPR: 2

User-Agent Client Hints の使用場所

簡単な答えとしては、User-Agent ヘッダーを解析するインスタンスや、同じ情報にアクセスする JavaScript 呼び出しを使用するインスタンス(navigator.userAgentnavigator.appVersionnavigator.platform など)をリファクタリングして、代わりに User-Agent Client Hints を使用する必要があります。

さらに一歩進めて、ユーザー エージェント情報の使用方法を再確認し、可能であれば他の方法に置き換える必要があります。多くの場合、プログレッシブ エンハンスメント、機能検出、レスポンシブ デザインを使用して同じ目的を達成できます。ユーザー エージェント データを使用する際の基本的な問題は、検査対象のプロパティと、それによって有効化される動作との間のマッピングが常に維持されることです。包括的な検出を最新の状態に保つためには、メンテナンスのオーバーヘッドが発生します。

これらの注意事項を念頭に置いて、サイトの User-Agent Client Hints リポジトリの有効なユースケースのリストをご覧ください。

User-Agent 文字列はどうなりますか?

既存のユーザー エージェント文字列によって公開される識別情報の量を減らすと同時に、既存のサイトに不当な中断を引き起こさないようにすることで、ウェブ上での隠れたトラッキング機能を最小限に抑える予定です。User-Agent Client Hints の導入により、ユーザー エージェント文字列に変更を加える前に、新しい機能を理解して試す機会が得られます。

最終的に、ユーザー エージェント文字列の情報は削減され、従来の形式が維持され、デフォルトのヒントと同じ上位レベルのブラウザと重要なバージョン情報が提供されます。Chromium では、エコシステムが新しいユーザー エージェント Client Hints 機能を評価するための期間を設けるため、この変更を少なくとも 2022 年まで延期しています。

このバージョンをテストするには、Chrome 93 から about://flags/#reduce-user-agent フラグを有効にします(注: Chrome 84 ~ 92 のバージョンでは about://flags/#freeze-user-agent という名前でした)。これは、互換性上の理由から履歴エントリを含む文字列を返しますが、詳細はサニタイズされています。たとえば、次のようにします。

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

Sergey Zolkin 氏によるサムネイル、Unsplash より