Fetch Metadataリクエストヘッダについて (Sec-Fetch-*)

ブラウザがリソースをFetchするさいに、そのFetchに関するメタ情報をリクエストヘッダに付与するというのがFetch Metadataという仕様です。

この情報を用いれば、画像の読み込みのFetchで銀行用のAPIが叩かれるはずがないといった、明らかな不正なリクエストを検知することができるようになる。

毎度おなじみGoogleのMike West氏によって仕様「Fetch Metadata Request Headers」が書かれている。

以前「Sec-Metadataヘッダについて」で紹介したSec-Metadataをより改良したものです。

ヘッダを分けることでヘッダ圧縮の仕組みとも相性が良くなっています。

Example

以下のような、そのフェッチに関する情報を示すSec-Fetch-*ヘッダが付与される。

Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross
Sec-Fetch-User: ?F


なお、Chrome CanaryでGoogleのサイトにアクセスすると、すでにこのヘッダが付いていることが確認できた。
f:id:ASnoKaze:20190215012147p:plain

意味

Sec-Fetch-Dest

Sec-Fetch-Destは、リクエストの先が何であるかを示します。

現在は、下記のどれかになります:
"audio", "audioworklet", "document", "embed", "empty", "font", "image", "manifest", "object", "paintworklet", "report", "script", "serviceworker", "sharedworker", "style", "track", "video", "worker", "xslt", "nested-document".

Sec-Fetch-Mode

Sec-Fetch-Modeは、リクエストのモードを示します。

現在は、下記のどれかになります:
"cors", "navigate", "no-cors", "same-origin", "websocket"

Sec-Fetch-Site

Sec-Fetch-Siteは、リクエストイニシエータのオリジンと、リクエスト先のオリジンの関係を示します。

現在は、下記のどれかになります:
"cross-site", "same-origin", "same-site"

Sec-Fetch-User

Sec-Fetch-Userは、user activationによって行われたリクエストかどうかを示します。
user activationについては、「HTML StandardのActivation」を参照。

ブーリアンの値を取ります。

Signed Exchange Reporting for distributors について

Webサイトを一つに固めて署名して再配布可能にする、Web Packagingという仕組みがあります。

現在、Web Packagingは以下の3つの仕様からなっています。

AMPなどをより標準化された仕組みで実現するために、現在議論が進められています。

HTTPリクエストとHTTPレスポンスの対(HTTP exchanges)を署名したsxgというファイルを再配布するわけですが、このsxgファイルの検証エラーをユーザエージェントからレポートできるようにする「Signed Exchange Reporting for distributors」という議論がされています。

Signed Exchange Reporting for distributors

概要

publisherがarticle.htmlを署名して作成したarticle.html.sxgを、distributorが再配布します
f:id:ASnoKaze:20190211021908p:plain

  • distributorはpublisherからarticle.html.sxgを取得する
  • ユーザエージェントは、distributorに該当のリソースのリクエストを投げます
  • distributorはarticle.html.sxgを返します
  • ユーザエージェントはarticle.html.sxgの証明書および署名を検証します。
  • 署名の検証に失敗したユーザエージェントは、予め指定されたエンドポイントにその旨レポートをPOSTします。通常のレポート先は、distributorになるでしょう。
レポートのポリシー適応

Network Error Loggingの仕組みを用いて、distributorがレポートの送信先エンドポイントを指定します。

NELについては以前説明したとおりです。
asnokaze.hatenablog.com

このようなレスポンスヘッダで、このポリシーを適応しておきます

Report-To: {"group": "sxg-errors",
            "max_age": 10886400,
            "endpoints": [{ "url": "https://report.distributor.example/" }] }
NEL: {"report_to": "sxg-errors", "max_age": 2592000}

NELの仕様にもプルリクが出ている
https://github.com/w3c/network-error-logging/pull/100

レポートの内容

このようなレポートがPOSTされます。

{
  "type": "signed-exchange",
  "age": 1,
  "url": "https://distributor.example/publisher.example/article.html.sxg",
  "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) ...",
  "body": {
    "phase": "sxg",
    "type": "sxg.signature_verification_error",
    "status_code": 200,
    "referrer": "https://www.example/",
    "method": "GET",
    "sxg": {
      "outer_url": "https://distributor.example/publisher.example/article.html.sxg",
      "inner_url": "https://publisher.example/article.html",
      "cert_url": "https://distributor.example/publisher.example/cert",
    }
  }
}

bodyのtypeとかはその他にも増えるのかな?証明書チェーンの辿れない場合とか、証明書の有効期間が合わない場合とか、いくつかのパターンがありそうな気はする。

Delegated Credentials for TLS について

BoringSSLが「Delegated Credentials for TLS 」に対応したので、簡単に仕様を眺める。


Delegated Credentials for TLS 」の仕様は、もともとはTLS1.3の仕様の著者でもあるEric Rescorlaによって書かれていたようだが、すでに WG DraftとなっておりMozillaFacebook、Cloudflareらの人が共著となっている。

Delegated Credentials for TLS

他社の提供するリバースプロキシやCDNTLSを終端する際、自身のドメインの証明書と秘密鍵を渡す必要があります。その際に、本来のドメイン所持者から委譲する形でクレデンシャルを発行し、終端者はそのクレデンシャルを持ってしてTLSハンドシェイクを行えるようにしようというのが「Delegated Credentials for TLS 」です。

こうすることで、本来の証明書所持者が委譲するクレデンシャルをより適切な形でコントロールできるようになります。CAとのオペレーションなく、自由な有効期限と自由な署名アルゴリズムでクレデンシャルを発行することが出来ます。

おおまかな流れは以下のとおりです
f:id:ASnoKaze:20190127193905p:plain

  • 委譲する側が、最初にdelegated credentialを発行し、対応する秘密鍵とともにTLSを終端するサーバに渡します
  • TLSハンドシェイク
    • クライアントはこの仕様に対応していることを示すdelegated_credential TLS拡張を送信します。
    • サーバはTLSハンドシェイク中に証明書チェーンとdelegated credentialを送信する
    • クライアントはdelegated credentialを検証し、問題なければ通信を続けます。

delegated credential

delegated credentialは、有効期限と公開鍵をもつ署名されたで=たです。署名を検証することで証明書が正しく委譲されていることを確認できます。

Credential は以下の情報を含みます。有効期限と公開鍵などです。

      struct {
        uint32 valid_time;
        SignatureScheme expected_cert_verify_algorithm;
        ProtocolVersion expected_version;
        opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;
      } Credential;

DelegatedCredentialには、上記のCredentialと署名が含まれます

      struct {
        Credential cred;
        SignatureScheme algorithm;
        opaque signature<0..2^16-1>;
      } DelegatedCredential;

Lurk

以前書いたLurk(Limited Use of Remote Keys)という、秘密鍵を渡さずに第三者TLS終端をさせる仕様について書きました
asnokaze.hatenablog.com

このLurkとの違いは、ハンドシェイク中に鍵が必要な処理はバックエンドサーバが行っていましたが、Delegated Credentialsでは事前にdelegated credentialを渡せばTLS終端するサーバだけで処理が完結します。

f:id:ASnoKaze:20190127200856p:plain

HTTP/2 ORIGINフレームのセキュリティを改善する提案仕様

AkamaiのMike Bishop氏らから、「DNS Security with HTTP/2 ORIGIN」という提案仕様が出ている。簡単に読む

ORIGINフレームとは

ORIGINフレームとは、RFC8336で標準化されているHTTP/2の拡張フレームです。

HTTP/2では、複数のドメインへのリクエストでもコネクションを使い回すことができます。https://a.example.comに接続した後に、https://b.example.comへのリクエストする場合、IPが同じで、そのドメインでも証明書が有効な場合(ワイルドカード証明書)は、すでにあるコネクションを再利用できます。

しかし、本当はa.example.comのサーバはb.example.comのコンテンツを提供できないという場合があります。その場合、サーバはステータスコード421を返し、それを受け取ったクライアントは改めてb.example.comにコネクションを張り直してコンテンツを取得します。これでは実際にコンテンツを取得するのが遅くなってしまいます。

ORIGINフレームでは、そのサーバがコンテンツを提供できるドメインリストをサーバから通知することで、クライアントが既存のコネクション上でリクエストを送信できるドメインを知ることが出来るようにします。

こうすることで、より効率よくコネクションの再利用が出来るようになります。

フレーム構造などは以前書いた記事が詳しいです
asnokaze.hatenablog.com

DNS Security with HTTP/2 ORIGIN

このとき、クライアントはORIGINフレームにかかれているドメインであれば無条件に既存のコネクションを使って良いわけではありません。サーバが悪意を持って他のドメインをORIGINフレームに記載すれば、本来のドメインを所持するサーバへ行くリクエストを、自身に送らせることができます。

そのため、通常のコネクションを再利用する時と同様、そのドメインがコネクションを張ったときに取得した証明書でも有効であることを確認します。その他にもCTログやOCSPを確認して証明書の有効性を確認することを推奨しています。

しかし、ORIGINフレームの仕様(RFC8336)では、ORIGINフレームで提供されたドメインの名前解決をすることは義務付けられていません。それは、名前解決の遅延や、新たな名前解決はクライアントが接続しようとしているサイトをネットワーク運用者に晒すことになるため、クライアントに一任されています。

しかし、名前解決が必須でないことは問題であり、必須にしようというのが「DNS Security with HTTP/2 ORIGIN」の提案です。

想定攻撃

この提案では名前解決を用いない場合にある種の攻撃が容易になるとしています。

たとえば秘密鍵が漏れた場合などを想定しています。そのときに、攻撃者はそのドメインへのリクエストを、自身のサーバに誘導することがきでるようになります。

例えば、victim-server.example.com秘密鍵が流出した場合。

攻撃者は、まずユーザに自身のWebサーバにアクセスさせます、そのURLなんでも良く、SNS掲示板に貼ればアクセスさせることは容易です。その後、攻撃者のサーバはORIGINフレームでvictim-server.example.comのコンテンツが提供できるとクライアントに通知し、さらに「Secondary Certificate Authentication in HTTP/2」の仕様に乗っ取りvictim-server.example.comの証明書と、秘密鍵の所持を証明します。

そうすると、クライアントはvictim-server.example.comへのリクエストを攻撃者のサーバに送信することになります。victim-server.example.comが、twitterだったりgoogleだったりすると思うと恐ろしいですね。

名前解決を行っていれば、この攻撃を防ぐことができます。もちろん、名前解決の通信を改ざんできれば攻撃は可能ですが、ORIGINフレームを用いることで攻撃が容易になっているということです。

おわりに

上記のような攻撃を緩和するために、ORIGINフレームで名前解決を必須にしようという提案でした。

不正な証明書が発行されたり、秘密鍵が漏れた場合に、CTログやOCSPに反映されるまでの間に発生しうる攻撃に対して、どれまでの緩和策が必要なのかというのは難しい問題だなと感じました。

2018年 振り返り (300記事目)

簡単に2018年を振り返ります。

2018年

今年も52記事を書くことができました。興味を持っていただけた記事も多く嬉しく思います。
また夢でもあった「単著の本」を出せたことも良かったと思います。

一方で、年末はあまり記事がかけてなかったのと、実際に手を動かして試したり全然出来なかったためにどうも反省点の残る年という印象が強いです。
昨年よりもここに書けることが少ないのは残念です。

300記事目

ふと現在の投稿数が299ですので、この記事が300投稿目になります。

ありがたい事に参考になったというコメントを頂くことがあります。やりたい事をやって人の役に立てるというのは幸せだなと思いました。

来年

来年もHTTP, QUIC, WICGらへんを中心に仕様を読み漁りたいと思います。実際に手も動かしていきたいところです。

まずは、次世代WebカンファレンスでHTTP/3のセッションがございます。よろしくおねがいします。
nextwebconf.connpass.com

願わくは来年も誰かの役に立てますように

記事一覧 (降順)

QUIC,HTTP/3 の draft-17に関するメモ

12月8日に現在標準化が進められているQUICの仕様のdraft-17が出ました。

以下の記事で書いたように、"HTTP over QUIC" のHTTP/3への名称が含まれています。
asnokaze.hatenablog.com

その他にも幾つかの変更が含まれているのでざっと目を通す。

この draft-17 は「9th Implementation Draft」であり、2019年1月に東京で開催されるQUIC WGの中間会議において draft-17対応実装を持ち寄って相互接続性試験が実施される予定です。

チャーターに書かれている通り、2019年7月に次のステップとなるIESGへの提出に向けて作業が続けられています。

spin bit

QUICではAckなどの制御情報も暗号化されます。経路上からはRTTやパケットロスの観測はできないようになっています。トラブルシュートの役に立てるために、経路上でRTTを推測可能にするSpin Bitという仕組みが長らく議論されてきました。

仕組みはいぜん書いたとおりです。
asnokaze.hatenablog.com

IETF 103 でも議論されました。この機能の実装意思についても実装者によって大きく別れました (議事録より)

最終的には拡張機能であり、コアの仕様ではショートパケットヘッダに予約ビットを確保はする形になりました。
draft-17ではビットの予約とSpin Bitの仕様である「The QUIC Latency Spin Bit」への参照が付きました

packet header

上記Spin Bitの予約のほか、ロングパケットヘッダ、ショートパケットヘッダの1バイト目について整理がされました。また今までのパケット番号保護から仕組みが変わって、ヘッダ保護(header protection)が付きました。これによって、パケットヘッダの特定のフィールド及び、パケット番号が暗号化されます。接続中は同じヘッダー保護キーが仕様されます。

ロングパケットでは1バイト目の下位4bit (予約領域と、パケット長)

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+
   |1|1|T T|R R|P P|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ...

   +-+-+-+-+-+-+-+-+
   |1|1|T T|E E E E|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Version -> Length Fields                 ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

ショートパケットでは1バイト目の下位5bit (予約領域と、鍵フェーズ、パケット長)

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+
   |0|1|S|R|R|K|P P|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ....

   +-+-+-+-+-+-+-+-+
   |0|1|S|E E E E E|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |               Destination Connection ID (0/32..144)         ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

両方のパケットで、上記続くパケット番号もマスクされます。
TLSレイヤから得られたシークレットから導出するヘッダ保護鍵と、パケット保護を行いサンプリングされた値を入力として得られたビット列をマスク値として使用します。

off-path migration attacks対策

経路上でパケットを観測し、複製したパケットを本来のパケットよりも早く届けることで、アドレスが変わったと思わせることが出来る。これによって、以降のパケットが観測者の経路を通るようになる。PATH_CHALLENGEを使って攻撃を緩和

その他トランスポートレイヤの変更
  • クローズ後のアンプ攻撃対策。未検証アドレスへのデータ送信量の制限
  • PMTUを1280以下に変更しない

などなど

HTTP/3

"HTTP/QUIC" から "HTTP/3" に名称が変更されました。その他にも幾つかの機能お改善が入ってます

優先度処理

HTTP/2の優先度制御はクライアントとサーバで状態を同期する必要があります。HTTP/3ではもともとコントロールストリーム上でプライオリティを扱っていましたが、HoLBとなるのでdraft-17で変更されました。

各リクエストストリームの最初にPRIORITYフレームを送信可能になったほか、exclusiveフラグが廃止されました。

DUPLICATE_PUSHフレーム

HTTP/2とは異なりパケットロスによりフレームが失われるかもしれないので、同じリソースのPUSH_PROMISフレームを複数送れるようになっていましたが、明示的なDUPLICATE_PUSHフレームが定義されました。

もともとの同一のリソースの複数のPUSH_PROMISフレームでは、同じリクエストヘッダであることが必要でしたが、それは今まで送られてきたPUSH_PROMISEフレームを覚えておく必要があることと同義でした。

DUPLICATE_PUSHフレームではリクエストヘッダは記述せず、すでに送ったPUSH IDのみであるため、そのような間違いは怒らなくなっています。

0長DATAフレーム

ペイロード長が0のDATAフレームが許可されました。その場合は、明示的な長さを持たずデータが終わるまで受信できるようになります。

最初から長さがわかってない場合のデータを返す場合に使用されるものと思われます。

新しいユーザエージェント表現 User Agent Client Hints の提案仕様

20200116追記
chrome://flags/ より、「Experimental Web Platform features」を有効にすることで使用できます
f:id:ASnoKaze:20200116161600p:plain


20191210追記
モバイルブラウザを示すブーリアン値をもつSec-CH-UA-Mobileヘッダが仕様に追加されました。


ユーザエージェントは歴史的背景により、複雑な文字列が使用されています。

iOSでは

     User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X)
                 AppleWebKit/605.1.15 (KHTML, like Gecko)
                 CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1

Edgeでは

  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
              AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.2704.79
              Safari/537.36 Edge/18.014

これには、不必要な情報や、歴史的な互換性を表現するための文字列などが含まれています。

ユーザエージェントを見て、適切なコンテンツを返すというのはこの複雑なユーザエージェントを正しく解釈する必要があります。

これを改善するためにClient Hintsの仕組みに則った新しい仕様である、「User Agent Client Hints」がGoogleのMike West氏から提案されています。

この提案では、ブランド名・プラットフォーム・アーキテクチャを個別に表現するヘッダを定義しています。

まずブラウザが https://example.comに最初にアクセスしたとき、Sec-CH-UAヘッダでブランド名とメジャーバージョンを含む情報を伝えます。

Sec-CH-UA: "Examplary Browser 73"

その後、サーバはさらなる情報が欲しい場合は、Client hintsの仕組みに乗っ取りAccept-CHレスポンスで欲しい情報を明示します。

Accept-CH: UA, Platform

サーバからのレスポンスを受け取ったブラウザは以後、次のヘッダを用いてユーザエージェント情報を伝えます。以下では、Sec-CH-Platformでプラットフォーム情報を送信しています。

     Sec-CH-UA: "Examplary Browser 73.3R8.2H.1"
     Sec-CH-Platform: "Windows 10"

新しく定義されるプロパティ

  • "brand" (for example: "cURL", "Edge", "The World's Best Web Browser")
  • "major version" (for example: "72", "3", or "28")
  • "full version" (for example: "72.0.3245.12", "3.14159", or "297.70E04154A")
  • "platform brand" (for example: "Windows NT", "iOS", or "AmazingOS")
  • "platform version" (for example: "10", "12", or "17G")
  • "platform architecture" (for example: "ARM64", or "ia32")
  • "model" (for example: "", or "Pixel 2 XL")

新しく定義されるヘッダ

各プロパティは下記のヘッダに含まれて送信されます。

  • Sec-CH-Arch
  • Sec-CH-Model
  • Sec-CH-Platform
  • Sec-CH-UA

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
            Chrome/71.1.2222.33 Safari/537.36
Sec-CH-UA: "Chrome 74.0.3424.124"
Sec-CH-UA-Platform: "macOS 12"
Sec-CH-UA-Arch: "ARM64"

ヘッダ先頭にSec-CH-が付く理由

W3C TAGのレビューに基づいています。
Sec-をつけることでJavaScriptからのアクセスを禁止し、CORSも不要となります。

github.com