より安全なリクエストヘッダー - Fetch Metadata Request Headers
ある日ページを開発していたとき、リクエストヘッダーの項目が正しいか確認するために開発者ツールを開いたところ、リクエストに見慣れない不審なヘッダーがいくつか増えていることに気づいた。
何かおかしい点に気づいただろうか?よく見てみると、なんと Sec-Fetch-* で始まるヘッダーが3つもある。
これが僕の注意を引いた。僕自身はこれらのヘッダーを設定していないし、他のライブラリを適用したわけでもないのに、なぜ勝手にヘッダーが追加されているのだろうか。おそらくブラウザの仕業だろうと推測した。
Googleで調べてみたところ、これは Fetch Metadata Request Headers という新しいドラフト仕様であることがわかった。今のところこれらのヘッダーを追加するのはChromeだけで、FirefoxやSafariでは付与されない。
再び同一生成元ポリシー(CORS)について
誰もが知っている通り、ブラウザは悪意ある人物が不正な手段でデータを盗み取るのを防ぐため、オリジンごとのアクセス条件を制限している。例えばクロスオリジンリクエスト時には Access-Control-Allow-Origin の付与やPreflightリクエストが必要だったりする。詳細については、以前僕が書いた「如何和 CORS 與 Cookie 打交道」を参照してほしい。
それでもなお欠陥はある。例えば <img/> は依然としてクロスオリジンリクエストを送信できるため、悪意ある人物はCORSの制限を回避できてしまう。もちろん、サーバー側で基本的な防御策を講じていれば、安全でないリクエストの大部分は防ぐことができる。
もしリクエスト送信時に、そのリクエストがどこから来たのかを説明する追加のヘッダー情報(メタデータ)を付加できれば、サーバーもヘッダーに基づいて適切なレスポンスを返すことができる。こうして生まれたのがこのドラフト仕様だ。
ヘッダー仕様:
現在、ドラフトには主に4つのヘッダーが含まれている:
1. Sec-Fetch-Dest
このリクエストの送信先(宛先)がどこかを表す。
取りうる値には audio、document、font、image、object、serviceworker など がある。これにはいくつかのメリットがあり、これらのヘッダーによる判定があれば、サーバーはそのリクエスト元が正当かどうかを即座に把握できる。例えば、リクエスト元が <img> なのにサーバーに対して画像を要求していない場合、十中八九攻撃者であるため、直接エラーを返すことができる。
2. Sec-Fetch-Mode
リクエストのモードを表す。主に cors、navigate、nested-navigate、no-cors などがあり、そのリクエストのモードが何であるかを判断する。fetch における mode に似ている。
例えば Set-Fetch-User を通じて、ユーザーが操作(クリックやキーボードなど)によってリクエストを発行したかどうかも知ることができる。
3. Sec-Fetch-Site
リクエストの送信元が同一オリジンかクロスオリジンかを表す。
4. Sec-Fetch-User
このヘッダーの値は真偽値(boolean)であり、リクエストが navigation request(リクエストの送信先が document)かつユーザーのインタラクション(ボタンの押下やキーボード操作など)があった場合にのみ true になる。サーバーはこのヘッダーに基づいて、ユーザーがリクエストをトリガーした方法が正当かどうかを判断できる。
例えばフォームの送信をクリックした際、document からリクエストが送信され、かつユーザーのインタラクション(ボタンクリックによる送信)があるため、Set-Fetch-User は ?T となる。
ヘッダーを削除する方法はあるのか?
正式な仕様の一部となった場合、これを取り除くことはおそらくできないだろう。また、これらは forbidden headers の一種であるため、手動設定でヘッダーの値を書き換えることもできない。
結論
従来このような対策を行うにはカスタムヘッダーを使って防御するのが一般的だったが、偽造される可能性が残る上に、カスタムヘッダーを追加するとPreflightのチェックが必要になり、手間が一つ増えてしまっていた。仕様レベルで導入されるメリットは、これらのヘッダーを容易に改ざんできず、統一された標準が提供される点にある。これらのヘッダーがあれば、バックエンドがリクエストを受信した際により多くの判断を行えるようになり、セキュリティを向上させることができる。
関連記事
- Three.js で僕の部屋を表現する 僕が React Three Fiber を使って実際の自分の部屋をブラウザ上に再現し、実世界のオブジェクトを目録に見立て、空間の記憶を通してここ数年の生活と仕事について語った話。
- フロントエンドで画像を扱う際に注意すべきこと Jake Archibaldの記事を起点に、現代のレスポンシブ画像の書き方を整理する。なぜwidth/heightを付ける必要があるのか、CSSのaspect-ratioはいつ使うべきか、AVIFとWebPの選び方、そしてpicture/source/srcsetを使ったモバイル向け画像の切り替えについて。
- CSS field-sizing — たった1行のCSSでフォーム要素を自動リサイズする かつてtextareaの自動高さ調整は、JavaScriptでscrollHeightを監視するしかなかった。しかしCSSのfield-sizing: contentなら、わずか1行で代替でき、textarea、input、selectに対応している。本記事では従来のやり方のペインポイントと、field-sizingの使い方をまとめる。
- リンクの下線をもっと見栄え良くする:text-underline-offset デフォルトでは下線と文字が近すぎて、このスタイルを好まないデザイナーもいるし、僕自身もあまり綺麗ではないと感じていた。