【JavaScript】User-Agent Client Hints API で端末情報を取得する

[2023-12-02 更新]

ブラウザーがサーバーにリクエストのたびに送信している User-Agent 文字列は、ユーザーの PC やスマートフォンの環境を知る上では必要不可欠な情報でした。ところが、昨今のプライバシー保護の強化や、ユーザートラッキングを禁止する潮流から、User-Agent 文字列から詳細な情報が削除されるようになりました。今回は、それを解決する User-Agent Client Hints API について詳しく解説します。

User-Agent 削減の背景

この User-Agent 文字列から情報を削除することを User-Agent 削減(User-Agent Reduction)と呼びます。User-Agent 削減が行われるようになった背景には、プライバシー保護やユーザートラッキング禁止の潮流があります。

User-Agent 文字列は次のような情報が含まれます。次の例は、Windows 11 の Chrome の User-Agent 文字列です。

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36

ご覧の通り、User-Agent 文字列には、ブラウザーの種類やバージョン、プラットフォーム(OS)の種類やバージョン、そして、CPU アーキテクチャまで読み取れます(ただし上記は User-Agent 削減後の文字列のため、一部はダミーデータに置き換えられてます)。

このように、ユーザートラッキングを手助けするような情報が数多く含まれているだけでなく、OS やブラウザーの詳細なバージョン情報は何かしらのセキュリティ脆弱性を持っていることを判定する材料にもなりかねません。User-Agent 削減は、こういった背景から実施されるようになりました。

端末情報取得プロパティ

JavaScript から User-Agent 文字列を取得するには navigator.userAgent を使いますが、この文字列を分解して得られる個別の情報を習得するプロパティも用意されています。プロパティ名の部分には、MDN へのリンクも張っておきました。

プロパティ説明Firefox の例Chrome の例
navigator.appCodeName文字列 “Mozilla” 固定“Mozilla”“Mozilla”
navigator.appName文字列 “Netscape” 固定“Netscape”“Netscape”
navigator.appVersionブラウザーのバージョン“5.0 (Windows)”“5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36”
navigator.oscpuOS 名“Windows NT 10.0; Win64; x64”undefined
navigator.platformプラットフォーム名“Win32”“Win32”
navigator.product文字列 “Gecko” 固定“Gecko”“Gecko”
navigator.productSubビルド番号(固定値)“20100101”“20030107”
navigator.vendorベンダー名“Google Inc.”
navigator.vendorSub常に空文字列

上記のプロパティは User-Agent 文字列をうまく分割してくれてはいるものの、多くは固定値か無意味な値です。おそらく、あまり使われてはいないのではないでしょうか。そのせいもあってか、Mozilla は上記プロパティすべてを廃止予定にしており、その利用は非推奨としています。なお、上記プロパティの仕様は WHATWG HTML 仕様で規定されていますが、そこでは廃止予定になっていません。

どのブラウザーでも同じですが、上記プロパティから User-Agent 文字列を超える情報はほとんど得られません。余分に得られるとしたら、せいぜい vendor プロパティだけです。しかし、vendor プロパティから得られる情報は “Google Inc.” または “Apple Computer, Inc.” のいずれかです。さらに、Microsoft Edge は vendor プロパティで “Google Inc.” を返しますので、あまり役に立ちません。

productSub のビルド番号は固定値です。Chromium または WebKit 系ブラウザーでは固定で “20030107” を返し、Gecko 系ブラウザーでは固定で “20100101” を返します。

エントロピーとは

User-Agent 削減では、一般にはなじみのないエントロピー(entropy)という用語が使われています。エントロピーとは、もともとは物理学(熱力学)や統計力学の用語ですが、近年では情報理論でも使われる用語です。User-Agent 削減の文脈におけるエントロピーとは、情報理論のエントロピーと同じく、「情報量」という意味を持ちます。

User-Agent の話に戻すと、ユーザートラッキングに有意な精度のレベルを高エントロピー(high-entropy)、比較的無害な精度のレベルを低エントロピー(low-entropy)と呼びます。

User-Agent 削減では、User-Agent 文字列には低エントロピーの情報までを掲載し、高エントロピーの情報は掲載しない、または、ダミーの情報を掲載します。前述の Windows 11 の Chrome の User-Agent 文字列を改めて見てみましょう。緑の情報は低エントロピーの情報です。そして、赤の情報は高エントロピーの情報に該当するためダミーの値がセットされている可能性が高いことを表しています。

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36

低エントロピーの情報は、OS の種類(Windows)、ブラウザーの種類(Chrome)、ブラウザーのメジャーバージョン(116)です。一方で、高エントロピーの情報の情報は、OS のバージョン(10.0)、ブラウザーのマイナーバージョン(0.0.0)、そして CPU アーキテクチャ(x64)が該当します。この高エントロピーの情報は必ずしも実際の値になるとは限りません。上記の Chrome の場合、CPU アーキテクチャは実際の値なのですが、OS のバージョンとブラウザーのマイナーバージョンはダミーの値です。

いくつかのデバイスとブラウザーで User-Agent 文字列を見ると、Chrome と Edge 以外のブラウザーは Chrome よりは正直ですが、ブラウザーごとに一部の情報を独自に隠蔽していることが分かります。

[Windows 11 / Chrome 116.0.5845.111]
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36

[Windows 11 / Edge 116.0.1938.54]
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.54

[MacBook Air M1 / macOS Ventura 13.5 / Safari 16.6]
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15

[iPhone 13 Pro / iOS 16.6 / Safari 16.6]
Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1

[Pixel 6 Pro / Android 13 / Chrome 116.0.5845.93]
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36

[Pixel 6 Pro / Android 13 / Firefox 116.3.0]
Mozilla/5.0 (Android 13; Mobile; rv:109.0) Gecko/116.0 Firefox/116.0

User-Agent Client Hints の概要

User-Agent 削減は Google が主導していることもあり、2023 年 8 月時点で積極的に User-Agent 削減を行っているのは Chrome や Edge などの Chromium 系ブラウザーのみです。情報が隠蔽されてしまうと困るウェブサービスもあるでしょう。そのため、Google は User-Agent 削減によって隠蔽された情報をサービス提供者が収集する方法を用意しています。それが User-Agent Client Hints という仕組みです。

User-Agent Client Hints には 2 つの手段が用意されています。一つは HTTP リクエストヘッダーとレスポンスヘッダーで情報をやり取りする手段です。サーバーがこれまで行ってきた User-Agent 文字列からの情報収集の代替手段になります。この HTTP ヘッダーを使う方法については、この記事では割愛します。詳細は MDN をご覧になると良いでしょう。

もう一つは JavaScript の API です。これまで User-Agent 文字列から取得できた情報を用途ごとに分割して取得する API が用意されています。これが本記事で解説する API です。

これまで User-Agent 文字列を JavaScript から取り出すには、

User-Agent Client Hints API は、navigator.userAgentData から得られる NavigatorUAData オブジェクトに実装されています。

if ('userAgentData' in navigator) {
    const uadata = navigator.userAgentData;
    // NavigatorUAData オブジェクトが利用可能
} else {
    console.log('User-Agent Client Hints API が利用できません。');
}

以降、NavigatorUAData オブジェクトのプロパティとメソッドについて解説します。なお、NavigatorUAData オブジェクトは SSL ページでないと利用できませんので注意してください。非 SSL ページの場合、navigator.userAgentDataundefined を返します。

標準化動向

User-Agent Client Hints は Google が提唱したため、現在は Chromium 系ブラウザーにしか実装されていませんが、国際標準化団体 W3C にて標準化提案が行われています。

この記事を執筆している時点では、この仕様は Draft Community Group Report という状態であり、まだ標準化プロセスには進んでいません。他のブラウザーが User-Agent Client Hints に賛同しているかは分かりませんが、しばらくは Chromium 系ブラウザーに限定した機能と考えたほうが良いでしょう。

低エントロピー情報を個別に取得

低エントロピー情報とは、ブラウザーの種類、ブラウザーのメジャーバージョン、端末がモバイル端末かどうか、そして、プラットフォーム(OS)の種類を指します。これらの情報は NavigatorUAData オブジェクトのプロパティから取得できます。

プロパティ説明
mobileBooleanモバイル端末かどうかを返します。
platformStringプラットフォーム(OS)の名前を返します。
brandsArrayブラウザーの名前とメジャーバージョンを格納したオブジェクトの配列を返します。

実際に低エントロピーの情報を取得してみましょう。

// NavigatorUAData オブジェクト
const uadata = navigator.userAgentData;

// モバイル端末かどうか
console.log(uadata.mobile); // false

// プラットフォーム名(OS 名)
console.log(uadata.platform); // "Windows"

// ブラウザーの名前とメジャーバージョン
console.log(JSON.stringify(uadata.brands, null, '  '));

mobile プロパティは、モバイル端末なら true を、そうでなければ false を返します。platform プロパティは OS の名前を返します。OS のバージョンは返しませんので注意してください。

brands プロパティは、ブラウザー名とメジャーバージョンの情報を返しますが、それをリストで返します。前述のサンプルコードは次のような JSON を出力します。

[
  {
    "brand": "Chromium",
    "version": "116"
  },
  {
    "brand": "Not)A;Brand",
    "version": "24"
  },
  {
    "brand": "Google Chrome",
    "version": "116"
  }
]

低エントロピー情報をまとめて取得

低エントロピー情報を一気にまとめて取得したいなら、NavigatorUAData オブジェクトの toJSON() メソッドを使うと良いでしょう。

const data = navigator.userAgentData.toJSON();
console.log(JSON.stringify(data, null, '  '));

上記コードは次のような結果をコンソールに出力します。

{
  "brands": [
    {
      "brand": "Chromium",
      "version": "116"
    },
    {
      "brand": "Not)A;Brand",
      "version": "24"
    },
    {
      "brand": "Google Chrome",
      "version": "116"
    }
  ],
  "mobile": false,
  "platform": "Windows"
}

toJSON() メソッドは JSON 文字列を返すのではなく Object を返しますので注意してください。

高エントロピー情報の取得

高エントロピー情報の取得には、若干、手間がかかります。NavigatorUAData オブジェクトの getHighEntropyValues() メソッドを使いますが、なぜか非同期メソッドになっており、Promise オブジェクトを返します。さらにすべての高エントロピー情報が得られるのではなく、引数に欲しい情報のキーワードを指定する必要があります。

次のコードは、getHighEntropyValues() メソッドで得られるすべての情報を取得します。

(async () => {
    const data = await navigator.userAgentData.getHighEntropyValues([
        "architecture",
        "bitness",
        "fullVersionList",
        "model",
        "platformVersion",
        "wow64"
    ]);
    console.log(JSON.stringify(data, null, '  '));
})();

getHighEntropyValues() メソッドは、引数にキーワードを指定してもしなくても、低エントロピーの情報は返します。もしキーワードを指定しない場合は、空の配列を引数に指定しますが、その場合は、無理に getHighEntropyValues() メソッドを使わずに、前述の navigator.userAgentData.toJSON() を使う方が良いでしょう。

getHighEntropyValues() メソッドに与えた引数のキーワードと意味は下表のとおりです。

キーワード高低説明値の例
architectureStringCPU アーキテクチャ“x86”
bitnessStringCPU ビット数“64”
fullVersionListArrayブラウザー名とフルバージョンのリスト
modelStringデバイスのモデル名(機種名)“”
platformVersionStringOS バージョン“15.0.0”
wow64Booleanブラウザーが WOW64 で動作しているかfalse

上記コードは次のような結果をコンソールに出力します。getHighEntropyValues() メソッドに与えた引数のキーワードに対応する結果が含まれていることが分かります。

{
  "architecture": "x86",
  "bitness": "64",
  "brands": [
    {
      "brand": "Chromium",
      "version": "116"
    },
    {
      "brand": "Not)A;Brand",
      "version": "24"
    },
    {
      "brand": "Google Chrome",
      "version": "116"
    }
  ],
  "fullVersionList": [
    {
      "brand": "Chromium",
      "version": "116.0.5845.111"
    },
    {
      "brand": "Not)A;Brand",
      "version": "24.0.0.0"
    },
    {
      "brand": "Google Chrome",
      "version": "116.0.5845.111"
    }
  ],
  "mobile": false,
  "model": "",
  "platform": "Windows",
  "platformVersion": "15.0.0",
  "wow64": false
}

fullVersionList の内容は、低エントロピーの brands とほとんど同じですが、fullVersionList のブラウザーのバージョンにはメジャーバージョンだけでなく、マイナーバージョンもセットされます。

なお、Android の Chrome では model に機種名がセットされます。

{
  "architecture": "",
  "bitness": "",
  "brands": [ ... ],
  "fullVersionList": [ ... ],
  "mobile": true,
  "model": "Pixel 6 Pro",
  "platform": "Android",
  "platformVersion": "13.0.0",
  "wow64": false
}

まとめ

かつて User-Agent 文字列を分解して経験則的にブラウザーや OS を判定してきたことを考えると、 User-Agent Client Hints API はその面倒をすべて肩代わりしてくれてるため、重宝するのではないでしょうか。ただし、残念ながら現時点では Chrome や Edge などの Chromium 系ブラウザーしか対応していない点が残念です。Safari や Firefox も User-Agent Client Hints API をサポートしてくれるよう願うばかりです。

今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。

Share