ChromeのUser-Agentについて今から考える

# 初めに
この記事は「[【マイスター・ギルド】本物のAdvent Calendar 2022](https://adventar.org/calendars/7794)」3日目の記事です。


普段は主にフロントエンドを担当していたり、サーバー周りを触ったりしています、dashogoです。
最近になってChromeのUser-Agent文字列削減に伴う対応が必要となってきて、焦っています。なので、調査と情報の整理、そしてこの記事を見ていくださっている方に助けていただきたいと思いまして執筆しております。

# ChromeのUser-Agent文字列削減の概略
Chrome公式によると以下のように説明されています。
> Chrome では UA 文字列でデフォルトで共有される情報を削減し、サイトやサービスが必要な情報のみをリクエストできるようにうする User-Agent Client Hints（UA-CH）という新しい API を導入しています。この計画を最初に発表したのは 2020 年 1 月で、 段階的なロードマップを 2021 年 5 月に共有しました。この変更により、UA 文字列の情報の制限において、Chrome と他のブラウザの機能が同調されます。

> Chrome は 2022 年 10 月下旬から、デスクトップのブラウザの UA 文字列に含まれる OS バージョンやデバイス プラットフォームなどの情報を削減し始める予定
次のフェーズは 2023 年 2 月を予定としており、モバイルの Chrome ブラウザを対象としています

>削減の準備
フェーズ 1: M92 より、navigator.userAgent、navigator.appVersion、navigator.platform へのアクセスに関する警告を DevTools に表示します。
フェーズ 2: オリジン トライアルを開始し、サイトが最終形まで削減した UA 文字列でテストやフィードバックできる期間を、少なくとも 6 か月間継続します。
削減のロールアウト
フェーズ 3: 移行にさらに時間を要するサイトなどのために、逆オリジン トライアルを開始して、少なくとも 6 か月間継続します。
フェーズ 4: Chrome の MINOR.BUILD.PATCH バージョン番号を削減します（ "0.0.0" ）。これがロールアウトされると、デスクトップとモバイル OS で、逆オリジン トライアルをオプトインしていないすべてのページの読み込みに、削減版の UA 文字列が適用されます。
フェーズ 5: 削減版のデスクトップ UA 文字列と関連する JS API（navigator.userAgent、navigator.appVersion、navigator.platform）のロールアウトを開始します。これがロールアウトされると、デスクトップ OS で、逆オリジン トライアルをオプトインしていないすべてのページの読み込みに、削減版の UA 文字列が適用されます。
フェーズ 6: 削減版の Android モバイル（とタブレット）の UA 文字列と関連する JS API のロールアウトを開始します。これがロールアウトされると、Android で、逆オリジン トライアルをオプトインしていないすべてのページの読み込みに、削減版の UA 文字列が適用されます。
削減の完了
フェーズ 7: 逆オリジン トライアルが終了し、すべてのページの読み込みに、削減版の UA 文字列と関連する JS API が適用されます。

User-Agent文字列削除の対応は段階的な移行を予定しており、7つあるフェーズのうち現在は5であると思われます。完了は2023年5月とされていますので、もう目の前まで来ている印象です。

https://developer.chrome.com/ja/blog/user-agent-reduction-oct-2022-updates/

https://developers-jp.googleblog.com/2021/06/chrome-user-agent.html

# User-Agent文字列について
User-Agent文字列はユーザーの使用しているOSやブラウザーに関する情報を含む文字列で、アクセス先のwebサーバーやスクリプトに伝えられます。
基本的な構文は以下の通りです。普段目にしている方からしたらとても単純に見えると思いますが、それだけ自由度が高いことも伺えます。
```
User-Agent: <product> / <product-version> <comment>
```
ですが、現在は以下のような書式がよく使用されているようです。
```
User-Agent: Mozilla/5.0 (<system-information>) <platform> (<platform-details>) <extensions>
```
- Mozilla/5.0
Mozillaと互換性があるブラウザーであるということを示す一般的なトークンであり、歴史的経緯から現在はほぼ全てのブラウザーで共通です。
- system-informatio
ブラウザーが動作しているOS(Windows、Mac、Linuxなど）やデバイス（iPhone、iPad、Androidなど）の情報を示します。
- platform、platform-details
ブラウザーに備わるレンダリングエンジンの情報を示します。
- extension
その他のブラウザーの情報などを示します。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/User-Agent

https://ics.media/entry/200729/

# ユースケース
User-Agentはフロントエンド、バックエンド、サーバーサイド、様々なところで利用されます。自分がよく目にするのは以下の２点かと思います。
1. モバイル端末かpc端末かの判定
ユーザーからのリクエストに対するレスポンス、ユーザーに表示する画面、画面のスタイルなどを端末の種類によって変えたいときに利用することがあると思います。今は一昔前と比べると端末の種類は格段に多くなったことで、サービスを提供する側も選択肢が増えたように思います。そのため、開発側はより柔軟な対応を必要とされるため、端末の種類を考慮することは今や必須となっている印象です。

2. ブラウザーの種類の判定
ブラウザーによってはスタイルの当たり方が異なる場合があり、対応しているブラウザーの種類によっては個別に設定が必要になります。特にSafariでは他と異なる挙動を示すことが多いように見受けられます。他にも、Chromeでは対応しているが、Safariでは対応していないルールもあったりします。
各ブラウザーのシェア率を見ると、世界全体で見るとChromeが大半を占めますが、モバイル端末に限定するとSafariとChromeの差はかなり縮まります。また、モバイル端末における各ブラウザーのシェア率は、日本だけで見るとSafariの方が高くなっているようです。そのため、Safariは無視できない存在です。他にも様々なブラウザーが存在していることも事実です。

https://gs.statcounter.com/

上記の２点は「ユーザーエージェント・スニッフィング」と呼ばれるもののようです。

https://ja.wikipedia.org/wiki/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%E3%83%BB%E3%82%B9%E3%83%8B%E3%83%83%E3%83%95%E3%82%A3%E3%83%B3%E3%82%B0


上記の他にも、ボットと悪意のある攻撃を識別するためにこの情報を利用するケースもあったりするようです。このような事情からUser-Agentの重要性を分かっていただけるかなと思います。

# 問題点
User-Agent文字列は非常に便利である反面、その便利さ故のリスクも持っています。Chrome公式では以下のように言及されています。
- 豊富で詳細なデータによってユーザーが特定される可能性
- ユーザーの知り得ないところでトラッキングが行われる可能性
- User-Agent文字列が長く複雑になったことで、文字列解析でエラーが発生

特に問題視されている点は、ユーザーの特定とトラッキングです。HTTPヘッダーには、User-Agent文字列を含め多くの情報が保持されています。そのHTTPヘッダーから取得できる情報をフィンガープリント（指紋）のように利用して、様々な情報からユーザーを特定して追跡するような行為を「ブラウザーフィンガープリント」と呼びます。「指紋」とあるようなプライバシー保護の観点や、悪意のある攻撃に利用される可能性があることから問題とされています。

https://developer.chrome.com/ja/docs/privacy-sandbox/user-agent/#ua-%E3%81%AE%E6%83%85%E5%A0%B1%E9%87%8F%E5%89%8A%E6%B8%9B%E3%81%A8-ua-ch-%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E7%90%86%E7%94%B1

https://www.w3.org/2001/tag/doc/unsanctioned-tracking/#unsanctioned-tracking-tracking-without-user-control

# 変わること
では具体的に何が変わるのでしょうか。Chrome公式では以下のように言及されています。

- ユーザーのプライバシーを向上させるために、ブラウザの User-Agent文字列で利用できる情報を削減する
- 情報量削減後のUser-Agentには、リクエストを送信したパソコンまたはモバイルのブラウザのブランドとメジャー バージョン、プラットフォームが含まれる
- その結果、いくつかのNavigatorインターフェース（navigator.userAgent、navigator.appVersion、navigator.platformなど）から返される値の情報量が削減される
- User-Agent文字列を解析することでデバイスのモデル、プラットフォームのバージョン、ブラウザのフルバージョンの情報を取得しているサイトの場合は、User-Agent Client Hints API の実装が必要になる

共通して取得可能な情報を基本的なものにすることで、ユーザーのプライバシー保護の向上が期待できます。また、これまでと変わり、User-Agent Client Hints API（以下「UA-CH」と呼びます）を使用することで情報を取得することができます。

具体的には以下のような情報を取得することができます。
- Sec-CH-UA：ブラウザの種類とメジャー/メジャーバージョン
- Sec-CH-UA-Mobile：モバイルデバイスを示す真偽値
- Sec-CH-UA-Platform：OSの種類

また、以下のようにも説明されています。
> If you are only checking the user-agent string for browser name, major version, or operating system, then your code will continue to work though you are likely to see deprecation warnings.

> While you can and should migrate to User-Agent Client Hints, you may have legacy code or resource constraints that prevent this. The reduction of information in the user-agent string in this backwards-compatible way is intended to ensure that while existing code will receive less detailed information, it should still retain basic functionality.

（翻訳）
「ブラウザ名、メジャーバージョン、オペレーティングシステムなどのユーザーエージェント文字列をチェックするだけなら、非推奨の警告が表示される可能性はありますが、コードは引き続き動作します。
User-Agent Client Hintsに移行することは可能であり、またそうすべきですが、レガシーコードやリソースの制約により移行できない場合があります。この後方互換性のある方法でユーザーエージェント文字列の情報を減らすことは、既存のコードがより詳細な情報を受け取らない場合でも、基本的な機能を維持できるようにすることを意図しています。」

このことから対応が難しいような場合でも削除後のUser-Agent文字列の情報を参照するに留まるのであれば、警告が表示されるものの、問題なく動作するようです。しかし、それはあくまで一時的なものと思いますので、いずれにせよ対応が必要と考えられます。


https://developer.chrome.com/ja/docs/privacy-sandbox/user-agent/

https://developer.chrome.com/ja/blog/user-agent-reduction-oct-2022-updates/

https://web.dev/migrate-to-ua-ch/#strategy-on-demand-client-side-javascript-api

# User-Agent Client Hints
Chrome公式では以下のように説明されています。

- User-Agent Client Hints を使用すると、User-Agentのすべてのデータを利用できる
- ただし、サーバーが特定のデータに対する明確な必要性を能動的に宣言した場合に限る

こちらは主にバックエンドの処理に関する説明であるようです。具体的にはレスポンスヘッダーのAccept-CHに必要な情報を記述することで実現できるようです。例えば
```
Accept-CH: Sec-CH-UA-Model, Sec-CH-UA-Platform-Version
```
このように記載することで2回目以降のリクエストでSec-CH-UA-ModelとSec-CH-UA-Platform-Versionの情報を取得することができます。

https://developer.chrome.com/ja/docs/privacy-sandbox/user-agent/

https://tech.drecom.co.jp/ac2021-client-hints-update/

## JavaScript API
フロントエンドでUser-Agent文字列の情報を取得する際にはnavigator.userAgentを使用していましたが、navigator.userAgentDataを使用する必要があるようです。
```js
if (navigator.userAgentData) {
  // use new hints
} else {
  // fall back to user-agent string parsing
}
```

以下のようにすることで、モバイル端末かpc端末かの判定を行うことができるようです。
```js
const isMobile = navigator.userAgentData.mobile;
```

他にも色々と参照することができるようですが、ここでは割愛したいと思います。個人的な印象としては、モバイル端末かどうかはUser-Agent文字列から得られる情報から複合的に判断していたところを`.mobile`とするだけで参照することができることは嬉しく思います。

https://web.dev/migrate-to-ua-ch/#strategy-on-demand-client-side-javascript-api

# 考えること
システム毎に事情は異なるものの、今回の変更は影響範囲が広く対応は余儀なくされると思います。では、どのような観点から考える必要があるのかですが、W3C Community Group Draft Reportによるとフロントエンドに関しては以下のように説明されていたりします。
> If you don't know if or where user-agent data is being used, consider searching your front-end code for use of navigator.userAgent and your back-end code for use of the User-Agent HTTP header. You should also check your front-end code for use of already deprecated features, such as navigator.platform and navigator.appVersion.

（翻訳）
「ユーザー・エージェント・データが使われているかどうか、あるいはどこで使われているかが分からない場合は、フロントエンド・コードでnavigator.userAgentが使われているか、バックエンド・コードでUser-Agent HTTPヘッダーが使われているかを検索することを検討してみてください。また、フロントエンドのコードで、navigator.platformやnavigator.appVersionのような既に廃止された機能を使用していないか確認する必要があります。」

> From a functional point of view, think about anywhere in your code where you are recording or processing:
>
>Browser name or version
Operating system name or version
Device make or model
CPU type, architecture, or bitness (for example, 64-bit)
It's also likely that you may be using a third-party library or service to process the user-agent. In this case, check to see if they are updating to support User-Agent Client Hints.

（翻訳）
「機能的な観点から、コードの中で記録や処理をしている場所を考えてみましょう。
ブラウザの名前またはバージョン
オペレーティングシステムの名前またはバージョン
デバイスのメーカーまたはモデル
CPUの種類、アーキテクチャ、またはビット数（例：64ビット）
また、ユーザーエージェントを処理するために、サードパーティのライブラリやサービスを使用している可能性もあります。この場合、それらがUser-Agent Client Hintsをサポートするように更新されているかどうかを確認してください。」

これらの説明からも分かるように自前のプログラムであれば修正は比較的容易ですが、サードパーティーやライブラリに関しては対応されていなければ、影響範囲にもよりますが、その利用自体を見直す必要もでてくる可能性があると思います。こちらの記事には有益な提案が多数みられるのでぜひ参考にしていただければと思います。

https://wicg.github.io/ua-client-hints/#os-specific-styles-use-case

## jQuery
特に直近で悩んでいることにjQueryやそのプラグインのソースファイルへの対応があります。例えば、バージョン1.5.2では、[jquery-1.5.2.min.js](https://ajax.microsoft.com/ajax/jQuery/jquery-1.5.2.min.js)を利用します。この中で一部```navigator.userAgent```（下記画像）が使用されています。どのような用途で使用されているかははっきりとしていませんが、実際にこのように古いバージョンのjQueryが利用されている可能性が高いです。
![スクリーンショット 2022-12-04 19.52.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2646829/14d17e94-8d23-f6f5-10bf-f212959dfe8b.png)
```navigator.userAgent```は即座に利用できなくなるわけではないと思いますが、```navigator.userAgentData```に移行していくことは明白なので、いずれにせよ何かしらの対応は必要と考えています。しかし、バージョンが古いことも関係していますが、公式な対応について情報が見つかっておりません。そのため、現状思いつく対応は以下のようなところかと考えています。
- バージョンを上げる
- コードを読み解いて、```navigator.userAgentData```を使用するように書き換える

また、修正できたとしても、影響範囲について検討が必要で、その範囲によっては確認作業に時間を要するとも考えています。
どなたかこの記事をご覧の方でこちらの対応について知っていることや良いアイディアがあればコメントいただけると大変嬉しいです。ぜひよろしくお願いします。

# まとめ
実際に色々と調べてみて頭の中の情報がすっきりとして良かったです。これからも調査・検討することが多くありますが、頑張りたいと思います。同じような方にとって何かヒントを提供することになれれば幸いです。