なぜ調べたか
Praha Challengeというプログラミング・ブートキャンプに参加することになりました。最初の課題として、よく使うHTTPヘッダについて調べて、クイズ作ってペア(他の参加者の人)に出題するというものに取り組みました。
HTTPヘッダって何なのかとかどんなのがあるのかみたいなのは本読んだりしたことはあるのですが、学んだことまとめてみたりクイズ作ったりっていうアウトプットはしたことなかったので、学びになりました。
せっかくなので調べた内容をまとめて記事にしようと思いました。
HTTPヘッダの重要性
HTTPヘッダは、HTTPのメッセージボディに対する付加的な情報(いわゆるメタデータ)を表現するものです。クライアントやサーバーはHTTPのメッセージを受け取ったら、まずヘッダを見てボディに対する挙動を決定します。
リソースへのアクセス権を設定する認証、クライアントとサーバーの通信回数と量を節約するためのキャッシュなどはヘッダの情報があって実現する機能です。ヘッダの種類や扱い方を理解しておくことは、WEBサービスを扱う上で避けては通れません。
主要なHTTPヘッダ
Host
HTTPリクエストが送信される先のサーバーのホスト名とポート番号を指定します。
Host: <host>:<port><host>… サーバーのドメイン名 (例)example.jp<port>… 指定するポート番号(指定されなかった場合、HTTPSのURLであれば443、HTTPのURLであれば80とみなされる)
Hostはすべてのリクエストメッセージで必ず指定する必要があります。
Content-type
そのメッセージのボディの内容がどのような種類なのかを、MIME(Multipurpose Internet Mail Extensions)という電子メールの仕様から拝借してきた仕様を使って表現するヘッダです。
Content-Type: <type>/<subtype>; charaset=<charaset><type>/<subtype>… メディアタイプ。MIMEの仕様。<type>… タイプ。リソースの表現の種類をざっくり指定する。RFC2046で9つ定義されている。(例)application<subtype>… サブタイプ。タイプに紐ついてリソースの表現の種類を詳細に指定する。(例)xhtml+xml<charaset>… リソースの文字エンコーディングの指定。省略可能。
タイプの一覧
| タイプ | 意味 | メディアタイプの例 |
|---|---|---|
| text | 人が読んで直接理解できるテキスト | text/plain |
| image | 画像データ | image/jpeg |
| audio | 音声データ | audio/mpeg |
| video | 映像データ | video/mp4 |
| application | その他のデータ | application/pdf |
| multipart | 複数のデータから成る複合データ | multipart/related |
| message | 電子メールメッセージ | message/rfc822 |
| model | 複数次元で構成するモデルデータ | model/vrml |
| example | 例示用 | example/foo-bar |
User-agent
リクエストを行うユーザーエージェントソフトウェア(ウェブブラウザなどのこと)のOS、ベンダー、バージョンを識別するためのヘッダのことです。
User-Agent: <product>/<product-version><comment><product>… 製品の識別子。(例)Mozilla<product-version>… 製品のバージョン情報。<comment>… 製品のより詳細な情報
例えばFirefoxのUser-agentは下記のようになります
Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0Mozilla/5.0は、Mozillaと互換性があるブラウザであることを示しています(現在はほぼすべてのブラウザで共通)Windows NT 6.1; Win64; x64; rv:47.0は、Windows7で動いていることを示しています。rvはGecko(後述)のバージョンです。Gecko/20100101は、HTMLレンダリングエンジンGeckoを搭載したブラウザであることを示しています(20100101は固定文字列)Firefox/47.0は、ブラウザーがFirefox47.0であることを示しています。
Accept
メディアタイプや文字エンコーディングは、サーバーが一方的に決定するだけではなく、クライアントと交渉して決めることができ、この手法はコンテントネゴシエーションと呼ばれます。Acceptは、クライアントが処理できるデータの種類をサーバーに通知するヘッダです。
Accept: <type>/<subtype>,<type>/<subtype>, ..., <type>/<subtype>;q=<qvalue>,...<type>/<subtype>… クライアントが処理できるメディアタイプをMIMEタイプで伝えます。サーバーは、この提案のうち一つを選択します。;q=<qvalue>… メディアタイプの優先順位を決める重みで、0〜1の数値で表します。数値が大きいほうが優先されます。
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8この場合、text/html、application/xhtml+xml、image/webpはデフォルトの1で最優先、application/xmlが0.9、それ以外が0.8という優先度です。
Referer
現在リクエストしているページへリンクしていた、前のウェブページのアドレスです。どこのウェブサイトからリンクされてきたかを表しています。
Referer: <url><url>… 直前のページのURIです。
Refererヘッダについては、詳細をユースケースとして後述します。
Accept-Encoding
コンテントネゴシエーションを使用して、クライアントが処理できるコンテンツのエンコーディング(圧縮アルゴリズムなど)をサーバーに通知するヘッダです。サーバーは、提案されたものから一つを選択して使用し、Content-Encodingヘッダを使用してクライアントに選択結果を知らせます。
Accept-Encoding: <encoding>,<encoding>, … ,<encoding>;q=<qvalue>, …<encoding>…gzip,compress,deflateなどの圧縮形式を指定したり、identityを設定してエンコードしないことを指定することもできます。また、qvalueで優先順位を決めることもできます。
Authorization
ユーザーエージェントがサーバーから認証を受けるための証明証を保持するヘッダです。ユーザーエージェントからのリクエストが送られたあとにサーバーから401 Unauthorizedが返ってきた場合(アクセスしたページの表示に認証が必要だった場合)、ユーザーエージェントの次のリクエストで使用される(認証に必要な情報を送信する)ことが多いヘッダです。
Authorization: <type> <credentials><type>… 認証の種類で、BasicやDigest、Bearerなどが設定される。<credentials>… 認証情報が設定される。例えば<type>にBasicが設定された場合、コロンで結合したユーザー名とパスワードをBase64でエンコードした文字列が設定される。
Authorizationヘッダについては、詳細をユースケースとして後述します。
Location
サーバーがリクエストを受信したとき、ユーザーエージェントが要求したURLとは別のURLにリダイレクトさせたい場合にレスポンスに設定するヘッダです。
Location: <url><url>… リダイレクトさせたい先のURL。
ユースケース
Refererヘッダのリスクについて
Refererヘッダによって送られる情報は、HTML要素に特定の属性を設定したり別のヘッダを設定することで制御することができます。
なぜRefererによって送られる情報の制御が必要なのか?
Refererヘッダには、プライバシーとセキュリティのリスクがあります。Refererへの情報の渡し方によっては、ユーザーの機密情報が漏洩してしまう可能性があります。たとえば、データの送信にPOSTではなくGETを使用し、クエリパラメータにユーザーのパスワードなどを載せた場合、ユーザーのパスワードはクエリパラメータとしてwebサーバーのアクセスログ・ファイアウォールのログ・プロキシサーバーのキャッシュやログ・ブラウザのキャッシュや履歴など、さまざまな場所に記録されてしまいます。
上記のようなリスクを軽減するために、下記のような対策を取ることができます。
- 漏洩してはまずい情報の送信にはPOSTを使用する
- 通信プロトコルはHTTPではなくHTTPSを使用し、通信を暗号化する
Referrer-Policyヘッダを使用し、Refererヘッダによって送られる情報を制御する(後述)imgやaなどのHTML要素のrel属性にno-referrerを設定して、Refererヘッダによって送られる情報を制御するimgやaなどのHTML要素のreferrerpolicy属性を使用し、Refererヘッダによって送られる情報を制御する(2020年12月29日時点でMDN Web Docsでは「実験的な機能」とされている)
Referrer-Policyヘッダによるリファラー情報の制御
Referrer-Policyヘッダを使えば、Refererによって送られるリファラー情報を制御することができます。
Referrer-Policy: <policy>-
<policy>… どのような制御を行うかを設定できる。下記のpolicyが利用できる。no-referrer… すべてのリファラー情報が省略される。no-referrer-when-downgrade…Referrer-Policyが設定されていない場合はこのpolicyが既定値として設定される。HTTPからHTTPへの送信やHTTPSからHTTPSへの送信などプロトコルのセキュリティ水準が同一である場合、もしくはHTTPからHTTPSへの送信など改善される場合は、URLのオリジン・パス・クエリ文字がリファラーとして送信される。HTTPSからHTTPへの送信などプロトコルのセキュリティ水準が低下する場合は、すべてのリファラーが省略される。origin… 文書のオリジンのみが送信される。オリジンとは、URLのスキーム・ホスト・ポートによって定義される(パスはオリジンに含まれない)。origin-when-cross-origin… 同一オリジン間でリクエストを行う場合、オリジン・パス・クエリパラメータを送信する。同一オリジンでない場合はオリジンのみ送信する。same-origin… 同一オリジン間でリクエストを行う場合、リファラーを送信する。同一オリジンでない場合はすべてのリファラー情報が省略される。strict-origin… HTTPからHTTPへの送信やHTTPSからHTTPSへの送信などプロトコルのセキュリティ水準が同一である場合、もしくはHTTPからHTTPSへの送信など改善される場合、文書のオリジンを送信する。HTTPSからHTTPへの送信などプロトコルのセキュリティ水準が低下する場合は、すべてのリファラーが省略される。strict-origin-when-cross-origin… 同一オリジン間でリクエストを行う場合、オリジン・パス・クエリパラメータを送信する。同一オリジンでない場合は、すべてのリファラーが省略される。unsafe-url…すべてのリファラー情報が送信される。
同一オリジンポリシーについて
前述のReferrer-Policyヘッダによるリファラー情報の制御では、「同一オリジン間でリクエストを行う場合」や「同一オリジンでない場合」など、同一オリジンかどうかという条件がいくつかでてきました。あるオリジンから読み込まれた文書やスクリプトについて、そのリソースから他のオリジンのリソースにアクセスできないように制限する仕組みのことを同一オリジンポリシーと呼びます。
オリジンの定義
オリジンは、URLのスキーム(http://, https://)、ホスト(example.com)、ポート(:80, :443)によって定義されます。ある2つのウェブコンテンツにおいて、スキーム、ホスト、ポートがすべて一致した場合、その2つのウェブコンテンツは同一オリジンであるといえます。
Authorizationヘッダによる認証について
リソースへのアクセスに制限がかかっている場合、Authorizationヘッダに認証情報を付与する必要があります。Authorizationヘッダで設定できる認証方法を3つ紹介します。
Basic認証
"<username>:<password>"をbase64でエンコードした文字列が認証情報として扱います。認証情報がデコード可能なので、データの盗聴などを防ぐためにはHTTPSプロトコルを利用して通信を暗号化するなどの対応が必要です。
Digest認証
サーバーから送られてきたランダムな文字列(nonce)とパスワードを組み合わせてハッシュ値を生成し、それを認証情報として扱う認証方式です。認証情報は暗号化されていますが、メッセージのボディは暗号化されないため、メッセージのボディの盗聴を防ぐためにはHTTPSプロトコルを利用して通信を暗号化するなどの対応が必要です。
Bearer認証
事前に入手したトークン(Bearer token)をAuthorizationヘッダに設定してリクエストを送り、サーバがそれを確認することで認証を行う認証方式です。クライアントが事前に入手するトークンは、認可サーバーに要求して発行してもらう方式(OAuth)で取得します。
Cache-Controlヘッダによるキャッシュの制御について
キャッシュとは、取得した情報を再利用するための仕組みです。キャッシュをうまく利用すれば、クライアントとサーバーの通信回数と量を節約することができます。Cache-Controlヘッダを利用すれば、情報の検証(情報を再取得すべきかどうかを判断する)と再取得(キャッシュを利用せず改めて情報を取得する)の条件を設定できます。
no-store
Cache-Control: no-storeを指定すると、レスポンスをキャッシュに保存できなくなります。
max-age
Cache-Control: max-age=<seconds>を指定すると、キャッシュが保存されてから<seconds>秒が経過するまではリソースは古くないとみなされ、更新されなくなります。つまり、キャッシュの有効期限が設定できます。設定した時刻が経過して以降にレスポンスを受け取ると、キャッシュは更新されます。
no-cache
Cache-Control: no-cacheを指定すると、格納されたレスポンスは、使用する前にかならず検証されます(有効期限が切れていなくても検証されます)。
must-revalidate
Cache-Control: must-revalidateを指定すると、格納されたレスポンスは、有効期限が切れている場合、使用する前にかならず検証されます。
参考文献
- https://developer.mozilla.org/ja/docs/Web/HTTP/Headers
- https://developer.mozilla.org/ja/docs/Web/Security/Referer_header:_privacy_and_security_concerns
- https://developer.mozilla.org/ja/docs/Web/Security/Same-origin_policy
- https://developer.mozilla.org/ja/docs/Web/HTTP/Authentication#Authentication_schemes
- https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control
- https://architecting.hateblo.jp/entry/2020/03/27/130535
- https://www.ryotosaito.com/blog/?p=264
- 書籍 Webを支える技術 山本陽平 技術評論社
