ChromeでUserAgentが凍結される日（User Agent Client Hintsの使い方）

# 追記（2020-05-06）
この記事は2020-02-10に執筆しましたが、その後急速に拡大したCOVID19の影響でスケジュールが変更になっています。現在判明しているリリーススケジュールは以下の通りです。

- 2021年まで従来通りのUser-Agent文字列が利用できる
- User Agent Client Hintsについては7月14日に安定版としてリリースされる予定のChromeバージョン84に導入予定

参考)
https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/-2JIRNMWJ7s/u-YzXjZ8BAAJ

(@devneko 様、ご指摘ありがとうございました！)

# UserAgentがGoogle Chromeで使えなくなる
「Google Chrome」の開発チームは1月14日、User-Agent文字列を非推奨とし段階的にUserAgent文字列の更新をやめるという計画を発表しました。これにより~~2020年9月中旬を目処に~~([COVID19の影響で早くても2021年まで延期となりました](https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/-2JIRNMWJ7s/u-YzXjZ8BAAJ))Chromeでは、PCやSPなどのデバイスの種類・OSのバージョンを問わず、同じUserAgent文字列が返却されるようになりそうです。

## UserAgentとは
UserAgentはブラウザがWebサーバーにデータを取りに行く際にサーバーに対して自動的に通知している、ブラウザの種類やバージョンやOSの種類やバージョンなどの情報を組み合わせた識別子のことです。
例えば、私が今利用しているGoogleChromeの開発者ツールをしようして `navigator.userAgent` を実行すると、以下のユーザーエージェントが表示されます。

```
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
```

[![Image from Gyazo](https://i.gyazo.com/1bf318ade3fcbe42515fbce39c37387e.png)](https://gyazo.com/1bf318ade3fcbe42515fbce39c37387e)

Webサービスを提供しているサーバーはこのUserAgentの情報を利用してWebブラウザーの種類やバージョンを判別し、挙動を変えていることがあります。

## 変更の背景
先に書いたようにUserAgentはサーバーでWebブラウザーの種類やバージョンを判別するために使われることがあるのですが、様々な弊害があることが指摘されていました。

### 個人の特定のために利用される可能性がある
AppleやGoogleが本格的に3rd Party Cookieの排除に乗り出し始め、Cookieによるユーザーの特定がどんどん難しくなっている中、Cookieを使わないでユーザーを特定する方法を模索する人たちがいます。
その方法の一つとして、「ブラウザーフィンガープリント」というWebブラウザーから得られる断片的な情報をつなぎ合わせて個人を特定する手法があります。

ブラウザーフィンガープリントという言葉は聞き慣れないと思うのですが、ブラウザから取得できる情報のうち、単体では何気ない情報を複数組み合わせることにより個人を特定する手法のことです。例えばあるユーザーが「20代女性である」という情報だけでは個人を特定することができませんが、「〇〇駅の近くに住んでいる」「△△社に勤めている」「エンジニアである」といった情報を組み合わせることで個人が特定できる可能性が高まります。ブラウザーフィンガープリントもこれと同じ話で、「ユーザーエージェント」「使用できるフォントの一覧」「端末のメモリ」「端末のCPUコア数」などブラウザから取得できる情報を組み合わせてCookieに頼らず個人を特定する技術です。

ユーザーエージェント文字列はブラウザーフィンガープリントで個人を特定しようとする際に重要な判断材料になってしまう可能性があります。このため、PCやSPなどのデバイスの種類・OSのバージョンを問わず、同じUserAgent文字列を返すようにすることで個人を特定できる情報をなるべく減らしたいというモチベーションがあります。

### 一部のブラウザで特定のWebサイトが正常に利用できなくなってしまうことがある
UserAgentはブラウザで特定の機能がサポートされているかどうかを判定するために利用されることがあります。
Chromeではサポートされているけれど、IEでは利用できない機能を使いたい場合、IEであるかどうかを判定し、IEでは特定の機能を利用できないようにする……ということが行われていることがあります。
本来は「その機能が使えるかどうか」を正しく判定して処理を変えるプログラムを書く必要があるのですが、古いサイトなどだとUserAgentを使って「このブラウザが送ってくるUserAgentには○○という文字が含まれていないからChromeではないだろう」という判定をしてしまうことがあるので、本来Chromeとほぼ同等の機能が使えるはずのブラウザで、正常に動作しないといったことが起こってしまっています。

もっとタチが悪いケースでは他社製のWebブラウザーを排斥するために、特定のUserAgentをもつブラウザに対して不便な挙動をするWebサービスがあるという指摘もあります。
その対策として、[あえて嘘のUserAgentをWebサーバーに送るようにする](https://gigazine.net/news/20191213-vivaldi-browser-user-agent-changes/)なんていう仕様になっているブラウザもでてきています…。

## 凍結スケジュール
記事執筆時(2020年2月)の凍結スケジュールを記載していましたが、COVID19の影響で大幅にスケジュール変更されそうなので一回コメントアウトしています。

<!-- 
[Google Chromeのブラウザエンジンの開発グループ](https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/-2JIRNMWJ7s/yHe4tQNLCgAJ)では凍結しやすい部分を凍結し、残りを徐々に統合することを計画しています。

| Chromeバージョン（リリース時期）      |       対応内容 |
|:-----------------|:------------------|
| Google Chrome 81（2020年3月中旬） | `navigator.userAgent` によるUAの取得が非推奨となる(`navigator.userAgent`を実行するとコンソールに警告が出るようになるそうです) |
| Google Chrome 83（2020年6月上旬） | ブラウザバージョンやOSバージョンなど、UserAgent文字列の中で後方互換性を損わずに固定できる部分を凍結したり、統一する |
| Google Chrome 85（2020年9月中旬） | UserAgent文字列を統一。デスクトップブラウザ用のUserAgentかモバイル用UserAgent文字列のいずれかしか取得できなくなる。 |
-->

# 代替方法
## User-Agent Client Hints
Chromeでは凍結するUserAgent文字列の代用機能として User-Agent Client Hints という機能を提供しようとしています。
まだ仕様は検討中のようですが[ドラフト仕様](https://wicg.github.io/ua-client-hints/)を見る限り、基本的にはブラウザのブランド名とメジャーバージョンのみしかデータを送ってくれません。さらに詳細な情報が欲しい場合は、サーバー側でレスポンスに `Accept-CH`というヘッダを含める必要があります。

Chrome80では既にUser-Agent Client Hintsを試してみることができます。
まずは `chrome://flags/` にアクセスし、 `Experimental Web Platform features` をEnabledにしてChromeを再起動してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45823/e3a80f55-f4c5-e62c-4d85-283fd1b52b67.png)

再起動した後、どこでもいいのでWebサイトにアクセスすると、Webサーバーに `sec-ch-ua: Google Chrome 80` というヘッダを送信するようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45823/197a0339-4311-9b53-7fe4-006571d8a7cc.png)

先ほどの書いた通り、サーバー側で何も対応しないと、プラットフォームなどの情報を取得することはできません。
さらに情報を取得するため、レスポンスに`Accept-CH`を含めたレスポンスを返却するWebサーバーを構築して確認してみたいと思います。

作ったWebサーバーのソースコードは[こちら](https://github.com/takayuki-ochiai/ua-client-hints-example)からダウンロードできるので、手元で動かしたいという人はご利用ください。Dockerとdocker-composeがインストールされているPCであれば、`docker-compose up`コマンドを実行するだけで `localhost:4567`にWebサーバーが立ち上がるようになっています。

Webサーバーの中身は単純なsinatraサーバーで、'Accept-CH'ヘッダに'UA, Platform, Arch, Model'という文字列を乗せているだけのものです。

<img width="1679" alt="スクリーンショット 2020-02-09 11.59.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45823/d521e1c6-a68e-ccb2-6a43-e55db4580c88.png">

この時点では何も追加の情報を得ることはできませんが2回目以降のリクエストでは、 `sec-ch-ua`だけではなく、`sec-ch-ua-arch`、`sec-ch-ua-model`、`sec-ch-ua-platform`といった情報が送信されるようになっているのが確認できます。

<img width="1680" alt="スクリーンショット 2020-02-09 12.13.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45823/36a4fe7e-b0d0-29ba-d9da-ccad818c4d7d.png">

単純に実装すると**初回のリクエストでは詳細情報を取得できなさそう**なので、ちょっと使いづらいかもしれません。

## JS APIによる取得

`navigator.getUserAgent` 関数が、Brand、 Platform、 Arch、 Model、 Versionといったデータが格納された `Promise<NavigatorUAData>` を返却してくれるようになるようです。Promiseなのでawaitか何かして使う必要がありそうですが…。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45823/329cc662-fb64-3ef1-f887-1474721362a6.png)


## 競合ブラウザの対応見込み(2020年2月6日現在)
Chromeは上記のようにUserAgent文字列を凍結し、代替機能としてUser-Agent Client Hintsを提供しようとしていますが、他のブラウザでは対応方針に違いがありそうです。このため、ブラウザによってはUserAgent文字列に依存し、他のブラウザに対してはUser-Agent Client Hintsに依存する必要があるという難しい状況が発生する可能性があります。

参考までに、現在の主要ブラウザの対応方針を記載します。今後変更される可能性も高いので、あくまで現時点のものと割り切ってご確認ください。

| ブラウザ      |       対応方針 |
|:-----------------|:------------------|
| Edge | [公式サポートの見込み](https://twitter.com/_scottlow/status/1206831008261132289) |
| Firefox | [UserAgent文字列の凍結は公式サポート。ただしUser-Agent Client Hintsは実装せずNavigatorUADataインターフェースを介したJS API呼び出しにのみ対応する方針](https://github.com/mozilla/standards-positions/issues/202#issuecomment-558294095)  |
| Safari | Safariはすでに部分的にUserAgent文字列を凍結している。かつてはUserAgent文字列を完全に凍結しようとしたが、様々な問題が発生して完遂には至らなかった模様。User-Agent Client Hintsへの対応方針は不明 |



# 参考文献
- [Intent to Deprecate and Freeze: The User-Agent string](https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/-2JIRNMWJ7s/yHe4tQNLCgAJ)
- [(UNOFFICIAL DRAFT)User-Agent Client Hints](https://wicg.github.io/ua-client-hints/)