11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

User-Agentと私

Last updated at Posted at 2020-12-01

この記事は、mediba Advent Calendar 2020 2日目です。
auスマートパスのバックエンドエンジニア(BE)、アーキテクト担当をしている北田です。
みんな大好きUser-Agent(以下UA)を通して今年を振り返り、来年を展望してみようと思います。

User-Agentは便利

auスマートパスでは、auブランドで提供している多彩な端末(以下デバイス)をサポートしています。

そのため、開発プロジェクトではデバイス要件を必ず確認するようにし、デバイス種別も定義していす。

これまでは、WebAppサーバにて、UAをキーにデバイス種別を判定し、HTMLテンプレートを変更する機能(デバイス種別判定機能)を実装していました。
社内ではよくあるパターンです。

デバイス種別判定機能をWebAppサーバに実装.png

auスマートパスは、2020年2月にUX向上を目的としてシステムの刷新を行い、BFFとREST APIサーバで構成されるようになりました。
これを機に、機能配置も見直し、デバイス種別判定機能を独立させました。
Lambda@Edgeでは、よくあるパターンかと思いますが、デバイス種別毎にオリジンを変更する機能を実装しました。

デバイス種別判定機能を独立させた.png

図を見て、富豪過ぎワロタと思われた方もいらっしゃるとは思いますが、その狙いや背景はまた後日...
デバイス種別判定機能を独立して開発、提供出来るようになったので、BFFと開発チームはそのリソースをUX向上に注力できるようなりました。
UAは、多彩な端末をサポートするauスマートパスには、便利で頼もしい存在です。

User-Agentはリスク

UAを頼りにデバイス毎に最適なUXの提供を図ってきたauスマートパスですが、リスクも明らかになってきました。

わがままボディ

各所で指摘されている通り、UAはわがままボディです。
以下は、2020年冬モデルの最新端末 Google Pixel 5に搭載されているChromeのUAです。

Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36 

何度見ても何を伝えたいのかわからない、わがままボディぷりです。
iOS端末に搭載されているauスマートパスアプリは、もっとわがままです。

Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148/smps-app/9.1.1

最近ですと、我らがCloudFront様が素敵なカスタムヘッダ(CloudFront-Is-Android-ViewerCloudFront-Is-IOS-Viewer)を用意してくれており、涙ぐましい正規表現もパースの結果で分岐するというアレな実装も必要がなくなりました。

とはいえ、OSバージョンの判定処理は、自前で組む必要があり、以前アレな実装を続けています。メンテナンス性は、高いとは言えません。

ゆるふわ

各所で指摘されている通り、UAはゆるふわです。
リクエストヘッダに付与されますので、クライアントからすると容易に偽装でき、サーバからすると容易に取得出来てしまいます。
クライアントサイドにおいても、フリーハンドで取得出来てしまいます。

console.log(navigator.userAgent)
# Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148/smps-app/9.1.10

デバイス種別という要件が存在し、その種別に応じたUIを提供しているauスマートパスでは、dev tool等での検証が容易であり、サーバサイドでの機能にUAを活かす事もできるメリットはあります。
しかし、リクエストヘッダの取得に何かしらの理由で失敗した場合、そのデバイス種別に対してUIを提供出来ないデメリットとリスクが存在します(2020年5月には、同様の理由で障害が発生してしまいました)。
また、以前から指摘されている通り、フィンガープリンティングに利用出来てしまい、プライバシー保護にも課題があります。
ゆるふわ故にウェブの理想から大きく逸脱した目的に用いられ、拡張され続けてきたとも言えそうです。

ウェブは使用しているブラウザーや機器に関係なく、誰からでもアクセスできるものです。
ユーザーエージェント文字列を用いたブラウザーの判定 - HTTP | MDN

User-Agentと未来の私

UAに依存するリスクは、各所の動向をフォローしても明らかです。
Googleは、UA文字列を凍結し、User Agent Client Hints(以下UA-CH)を提唱しています。

We want to freeze and unify (but not remove) the User Agent string in HTTP requests as well as in navigator.userAgent
Intent to Deprecate and Freeze: The User-Agent string
コロナ禍で、2021年に延期のステータスの様です

Mozillaは、様子見のポジションです。

The proposal is to build on top of the Client Hints infrastructure and solve these problems over time.
User Agent Client Hints · Issue #202 · mozilla/standards-positions

auスマートパスは、UAの課題に対して、どう取り組むべきでしょうか。
UA-CHの動作確認サイトとその実装から、サーバサイドの観点で考察してみます。

尚、UA-CHの動作確認は、Chrome v85 stable以上のバージョンと関連するフラグを有効にする必要があります。

サーバサイドUA-CH

まずは、動作確認サイトへのリクエストとレスポンスを確認してみます。

初回のリクエストでは、以下のようなヘッダが付与されています。
初回リクエスト.png

リクエストヘッダは、sec-ch-ua及びsec-ch-ua-mobileのみ設定されており、制限されていることが伺えます。
また、server.jsでもレスポンスヘッダ:accept-chを付与する実装を確認出来ます。

	res.set('Accept-CH', acceptCH.join(', '));
	header = 'Accept-CH: ' + acceptCH.join(', ');

2回目のリクエストでは、以下のようなヘッダが付与されています。

2回目リクエスト.png

レスポンスヘッダ:accept-chは、付与されておりません。
1回目のレスポンスにヘッダとして付与されていた、accept-chの値に基づいたリクエストヘッダが付与されています。

2回目のリクエスト処理の実装は以下の通り、リクエストヘッダを指定しているわけではなく、ブラウザがUA-CHの仕様に基づき付与していることが伺えます。

  fetch('/show-headers.json' + document.location.search)
  .then((res) => {
      if (res.status === 200) {
        res.json().then((data) => {
          let secChText = '';
          for (const header in data['Sec-CH']) {
            secChText += `Sec-CH-${header}: ${data['Sec-CH'][header]}\n`;
          }
          document.querySelector('.request').textContent = secChText;
        });
      }
  });

上記の挙動は、2020年12月現在のものであり、今後もアップデートが予想されます。
引き続き、フォローする必要がありそうです。

デバイス種別判定機能をUA-CHする

考察を踏まえ、デバイス種別判定機能は、どうあるべきでしょうか。
要件それ自体を見直す必要もありそうですが、UA-CHに適合していくのであれば、BFFは再びスマートになる必要があるのでしょうか。

デバイス種別判定機能をUA-CHする.png

JavaScript APIを用いたクライアントサイドでのUA-CHもあるので、時間をつくって、そちらも考察してみようと思います。

おわりに

UAは便利で息の長いウェブのレガシーですが、転換期にあります。
技術の境界線が変わろうとしていることを強く感じられるものでもあるので、引き続きフォローしていきたいと思います。
また、私はサーバサイドでの機能開発がメインのバックエンドエンジニアのポジションですが、auスマートパスでは、BFFの構築、運用にも携わっています。

技術の境界線を跨ぎ、ヒィヒィ言いながら貪欲に開発に取り組みたい仲間を募集していますので、興味が湧いた方、話だけでも聞いてみたい方は、是非エントリーをお願いします。

11
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?