QUICにおいてNAT検出を行う拡張フレームの提案仕様

QUICを使用している際に、その通信がNATによってアドレス変換を行われているかを検出する「QUIC Address Extension」という提案仕様がAppleの人らによって出されています。

具体的には、送信元IPアドレスがなんであるかを通信相手に確認します。そうすることで、途中でIPアドレスが変換されているかを確認することが出来ます。

NATがいなければ、NATリバインディングを避けるためにPINGフレームを送る必要もありません。一方で、NATを検出した場合はコネクションIDとポートを定期的に変えることがよりプライバシーを高めることになります。

概要

自身のIPでアドレスが相手にどのように見えているか確認する、「PUBLIC_ADDRESS_REQUESTフレーム」と「PUBLIC_ADDRESS_RESPONSE フレーム」を定義します。

f:id:ASnoKaze:20190317170136p:plain

  • PUBLIC_ADDRESS_REQUESTフレームを送信することで、自身のIPアドレスを相手に確認要求を出します。
  • PUBLIC_ADDRESS_REQUESTフレームを受け取った側は、相手のIPアドレスとポートをPUBLIC_ADDRESS_RESPONSEフレームにお繰り返します。

こうして、自身の送信元IPアドレスがなんであるかを確認することが出来ます。

もちろん、PUBLIC_ADDRESS_REQUESTフレームとPUBLIC_ADDRESS_RESPONSEフレームは暗号化されているため、第三者に見ることは出来ません。

その他

同様の拡張がTLSでも提案されています。「TLS Client Network Address Extension」という提案仕様で、著者は一部異なりますが同じくAppleの人らによる提案です。

HTTP/2をバイトストリームトランスポートとして利用する提案仕様

HTTP/2コネクション上で任意のバイトストリームをやりとりできるようにする「Using HTTP/2 as a Transport for Arbitrary Bytestreams」という仕様がAppleの人らによって提案されている。

IETF103でも議論(資料)になったが、その後 draft-01 が出ているのが現状である。

ユースケースとしては任意のバイトストリームをトンネリングしたり、一つのコネクション上で複数のバイトストリームをやり取りするのにストリームを利用するといった背景があるようだ。

通信の流れ

以前解説したWebsocket over HTTP/2と同じように、HTTP/2コネクションを確立したあとにストリーム上でConnectメソッドを用いる。
asnokaze.hatenablog.com

大まかな流れは以下の通り
f:id:ASnoKaze:20190313111855p:plain

  • SETTINGSフレームでお互いにこの仕様に対応していることを確認する。「SETTINGS_ENABLE_CONNECT_PROTOCOL」および「SETTINGS_ENABLE_BIDIRECTIONAL_CONNECT」パラメータを1にセットして交換する
  • クライアントはCONNECTメソッドで、:protocolヘッダにbytestreamもしくはdatagramを設定する
  • サーバ側は問題なければ200を返す
  • 以降そのストリームはbytestreamもしくはdatagram通信用となる。データはDATAフレームによって運ばれ、双方向に通信される。

bytestreamとdatagramの違いは、DATAフレーム毎に境界を持つかの違いである。datagramの場合はDATAフレームごとにデータの境界を持ち、アプリケーションに渡される場合にその境界を維持する必要がある。

Secondary Certificate Authentication in HTTP/2 という仕様について

目次

Secondary Certificate Authentication in HTTP/2

HTTP/2コネクションを確立したあとに、サーバまたはクライアントから追加の証明書を要求・提供できるようにする「Secondary Certificate Authentication in HTTP/2」という仕様が議論されています。(secondary-certsとも呼ばれています)

仕様自体はすでにwg draftとなっています。

数年前にこのブログでも紹介しましたが、デザイン変更もあるため再度簡単に読み直します。

用途

この仕様では、コネクションを確立したあとに、追加の証明書を要求提供できます。サーバ側から要求する場合と、クライアントから要求する場合の両方がありえます。まず、その用途について確認します。

サーバ側から証明書を要求するパターン

Webページでは、提供するページによってセキュリティレベルが異なる場合があります。管理者や、管理者の中でも上位者のみ閲覧できるページもあります。そういったページのみクライアント証明書を要求するといったパターンもあります。

一度コネクションが確立したあとに、追加でクライアント証明書が必要だという場合は、以前までであればTLS renegotiationを行ってクライアント証明書を要求していました。

しかし、HTTP/2ではrenegotiationを禁止しているため、確立したコネクション上で追加のクライアント証明書を要求することができませんでした。

クライアント側から証明書を要求するパターン

HTTP/2では一つのTCPコネクション上で、条件があえば異なるドメインへのHTTPリクエストを送信することが出来ます。例えば、ハンドシェイク中に*.example.comの証明書が得られた場合、かつサーバのIPアドレスが同じであればa.example.comとb.example.comを同じコネクション上でリクエストすることが出来ます。

一方で、「RFC8336 The ORIGIN HTTP/2 Frame」(詳細は過去記事参照)を用いることで、サーバ側から提供できるオリジンリストをクライアントに提供できます。ここでクライアントは、このオリジンのリストにあるサーバ証明書を追加で要求することが出来ます。

こうすることでより多くのドメイン名に対するリクエストで既存のコネクションを利用できるようになります。

また、一度確立したTLSコネクション上で追加の証明書を要求するので、SNIなどの情報によってHTTPリクエスト先が第三者にバレることもありません。

通信フロー

この「Secondary Certificate Authentication in HTTP/2」では4つの拡張フレームを定義しています。

  • CERTIFICATE_REQUEST: 追加の証明書を要求する (TLS1.3のCertificateRequestメッセージを含む)
  • CERTIFICATE_NEEDED: 追加の証明書が必要となっているストリームIDを示す
  • CERTIFICATE: 追加の証明書
  • USE_CERTIFICATE: 追加の証明書を使用するストリームIDを示す

CERTIFICATE_REQUESTフレーム及びCERTIFICATE_NEEDEDフレームの中身については、追加の証明書の要求及び提供する仕組みを定義する「Exported Authenticators in TLS」 (こちらはTLSの話)で定義されているメッセージを利用している

サーバ側から証明書を要求する場合

f:id:ASnoKaze:20190311005237p:plain

クライアントがストリームID N でクライアント証明書が必要な/protectへのリクエストを送信します。そこで、サーバ側からCERTIFICATE_NEEDEDフレームが送られ、クライアントは追加の証明書を提供し、無事そのストリームでHTTPレスポンスをつけとります。

クライアント側から証明書を要求する場合

サーバがORIGINフレームを用いて、提供可能なオリジンのリストをクライアントに通知します。クライアントはその中で必要な証明書を追加で要求します。追加の証明書が得られたら、そのオリジンのドメインに対してHTTPリクエストを送ります。

f:id:ASnoKaze:20190311005418p:plain

DigiCertによるプライベートアドレスの逆引き名の証明書誤発行

DigiCertの発行した証明書で、プライベートアドレス(192.168.1.)の逆引き名である「1.1.168.192.in-addr.arpa」を含む証明書が発行されていることが判明し話題となった。

すでに、revokeされているが、下記の通りSANsに「1.1.168.192.in-addr.arpa」が含まれていた。

  X509v3 Subject Alternative Name: 
      DNS:cynthia.re
      DNS:www.cynthia.re
      DNS:1.1.168.192.in-addr.arpa
      DNS:69.168.110.79.in-addr.arpa

https://crt.sh/?id=1231411316

最初の報告は、2月27日にmozilla.dev.security.policyメーリングリストに投稿された「Possible DigiCert in-addr.arpa Mis-issuance」である。報告者はFQDNが誤って検証されるかテストし、実際に発行されたと言っている。

直後に、DigiCertのJeremy Rowley氏によって調査を開始した旨が報告され、翌28日には誤発行された流れが報告されている。

また、すでに同様の流れで誤発行された証明書はないことが確認されている。

誤発行の流れ

Jeremy Rowley氏によって、調査中ではあるが誤発行の流れが報告されている

WHOIS情報を自動でパースできない場合は、検証エージェントによって手動でWHOIS情報をシステムにアップロードする。この際に、承認範囲をid-addr.arpaと設定したため、1.1.168.192.in-addr.arpaが発行可能となってしまっていた。とのことである。

また、機械的なチェックでもin-addr.arpaがすり抜けていた模様。

in-addr.arpaな証明書

そもそも、グローバル/プライベートを問わず逆引き名、*.in-addr.arpaに対する証明書発行は正しいのか、ユースケースがあるのか個人的には疑問である。

私自身詳しいわけではないが、上記スレッドでも明確な発行基準が存在してないような事が書かれている。

一方で、すでに*.in-addr.arpaに対する証明書は発行されており、CTを見るとすでにパブリックIPに対する逆引き名の証明書は発行されていることが確認できる

f:id:ASnoKaze:20190306005920p:plain
(Google Transparency Report)

その他に、www.175.232.77.in-addr.arpa (アクセス非推奨) といった実際にHTTPSがサービスとして動いてるドメインも存在する。

今後、 .in-addr.arpa に対する証明書はCA/B Forumなど適切な場所で議論にあがるかもしれないが、もし詳しい人がいたら是非教えていただければと思います。

その他のトピック

上記のスレッドで、関連していくつかの話が上がっているのでメモ程度に書いておく

  • WHOISを自動パース出来ない時があることが問題であるため、RDAPなどの利用や連携についての議論
  • IPアドレス証明書については、BGPSecなどで利用されている (https://1.1.1.1 といった例も)
  • PSLの利用

HTTP/3で接続してVPNとして使うMASQUEプロトコルの提案仕様

GoogleのDavid Schinazi氏が「The MASQUE Protocol」という仕様を提出している。

初版のdraftであり、議論の呼び水としての立ち位置が強いがまずは読む。

MASQUE Protocol

MASQUEはMultiplexed Application Substrate over QUIC Encryptionの略称です。この提案仕様では、例えばHTTP/3のQUICコネクションを確立したあとに、そのQUICコネクションをVPNとして使う例を上げています。もちろん、他のプロトコルも通信可能だと思います。

このMASQUEプロトコルの大きな特徴としては、第三者がMASQUEプロトコルの使用に気づかないところである。

  • クライアント・サーバ間の通信を見てもMASQUEプロトコルを喋ってることがわからない
  • 三者がサーバに通信を試みてもただのWebサーバにしか見えない (MASQUEプロトコルを喋るための鍵がないため)

もちろんHTTP/2 over TCPにフォールバックも可能だがパフォーマンス的には劣ることになる。

また、Datagramフレームの利用などを上げているが、具体的なVPN over QUICの仕組みには言及しておらず、方向性含め議論となると思われる。
(「QUICの信頼性のないデータグラム拡張(MESSAGEフレーム/Datagramフレーム)」)

HTTP/3として通信を開始したあとに、クライアントを認証したあとに、そのストリームはMASQUEプロトコル専用に移行します。
f:id:ASnoKaze:20190302143206p:plain

  • MASQUE通信を行うクライアントとサーバは事前に共通鍵を共有しておくか、サーバはクライアントの秘密鍵に対応する公開鍵をリストしておきます。
  • クライアントは通常のHTTP/3で、サーバに接続します。(ラベルEXPORTER-masqueを用いた、TLS keying material exporterで得られた値を後のノンスとして使用する。)
  • クライアントは/.well-known/masque/initialに対してCONNECTメソッドのリクエストを投げます。この際に認証に使う、Masque-Authenticationを付加します。公開鍵を使う場合と、共通鍵を使う場合で異なります
  Masque-Authentication: PublicKey u="am9obi5kb2U=";a=1.3.101.112;
  s="SW5zZXJ0IHNpZ25hdHVyZSBvZiBub25jZSBoZXJlIHdo
  aWNoIHRha2VzIDUxMiBiaXRzIGZvciBFZDI1NTE5IQ=="

  Masque-Authentication: HMAC u="am9obi5kb2U=";a=2.16.840.1.101.3.4.2.3;
  s="SW5zZXJ0IHNpZ25hdHVyZSBvZiBub25jZSBoZXJlIHdo
  aWNoIHRha2VzIDUxMiBiaXRzIGZvciBFZDI1NTE5IQ=="
  • サーバはMasque-Authenticationを見てクライアントを認証します
    • クライアントを正しく認証できなかった場合は、405 Method Not Allowedを返します。これは予期せぬCONNECTメソッドに対する通常の応答であるため、第三者が試みてもMASQUEプロトコルに対応してるかはわかりません。
    • クライアントを正しく認証できた場合は、101 Switching Protocolsを返してそのストリームをMASQUEプロトコル専用にします。

リバースプロキシのエラーを示す Proxy-Statusヘッダの提案仕様

CDNクラウドのロードバランサを使用するのは一般的です。これらのリバースプロキシは様々な理由により502 Bad Gateway504 Gateway Timeoutを返しますが、トラブルシュートするには情報が少ない場合があります。

また、追加の情報を示す場合においても、各社によって異なっています。

そこで、プロキシのエラー情報を示すProxy-Statusレスポンスヘッダを定義する「The Proxy-Status HTTP Header Field」という提案仕様がFastlyのmnot氏らより出されています。

初版のdraftだが読む。

Proxy-Statusヘッダはまずそのエラータイプが示され、Extra Parameters続きます(ない場合もあります)。

f:id:ASnoKaze:20190221012051p:plain
Proxy-Statusの例を示します。

   HTTP/1.1 504 Gateway Timeout
   Proxy-Status: connection_timeout; proxy=SomeCDN; origin=abc; tries=3

上記は、SomeCDNがオリジンであるabcに対して3回思考した後、connection_timeoutのエラーとなったことを示します。
proxy, origin, tries などがExtra Parametersです。

Proxy Status Types

現在はエラーとして様々なタイプが定義されています。

エラーごとに推奨されるステータスコードも併記されている。また、エラーごとに付与されるExtra Parametersが異なるが本記事では省略する。

  • dns_timeout: 宛先ホスト名の解決にタイムアウトした(504)
  • dns_error: 宛先ホスト名の解決エラーとなった(502)
  • destination_not_found: 適切なバックエンドを決定できなかった(500)
  • destination_unavailable: ネクストホップが利用できないと判断した(503)。ヘルスチェックがダウンしてる場合など。
  • destination_ip_prohibited: 宛先IPへの接続を禁止する設定になっている(502)
  • destination_ip_unroutable: 宛先IPへの経路を見つけることができなかった(502)
  • connection_refused: ネクストホップに拒否された(502)
  • connection_terminated: ネクストホップに切断された(502)
  • connection_timeout: ネクストホップへの接続がタイムアウトした(504)
  • connection_read_timeout: 期待すべきデータを待ったが上限にたっした(504)
  • connection_write_timeout: データを書き込もうとしたが出来なかった。バッファがはけなかった場合など(504)
  • connnection_limit_reached: コネクション数の上限に達した
  • http_response_status: 4xxや5xxのレスポンスを受け取った
  • http_response_incomplete: 受け取ったレスポンスが不完全(502)
  • http_protocol_error: HTTPプロトコルエラー(502)
  • http_response_header_block_size: HTTPレスポンスヘッダブロックが大きすぎる(502)
  • http_response_header_size: HTTPレスポンスヘッダのいずれかが大きすぎる(502)
  • http_response_body_size: HTTPレスポンスボディが大きすぎる
  • http_response_transfer_coding: レスポンスのtransfer-codingデコードエラー(502)
  • http_response_content_coding: レスポンスのcontent-codingデコードエラー(502)
  • http_response_timeout: HTTPレスポンスのタイムアウト(504)
  • tls_handshake_error: ネクストホップとのTLSハンドシェイクエラー(502)
  • tls_untrusted_peer_certificate: ネクストホップとのTLSハンドシェイクにおける信頼できない証明書エラー(502)
  • tls_expired_peer_certificate: ネクストホップとのTLSハンドシェイクにおける証明書有効期限切れ(502)
  • tls_unexpected_peer_certificate: ネクストホップとのTLSハンドシェイクにおける期待されない証明書のエラー(502)
  • tls_unexpected_peer_identity: ネクストホップとのTLSハンドシェイクにおける名前の不一致(502)。Subject Alternative Nameの不一致など
  • tls_missing_proxy_certificate: ネクストホップとのTLSハンドシェイクで証明書を要求したが設定されていなかった(500)
  • tls_rejected_proxy_certificate: TLSハンドシェイク中に得られた証明書を拒否した(500)
  • tls_error: ネクストホップとの通信中のTLSエラー(502)
  • http_request_error: オリジンに代わって、プロキシが400, 403などのステータスコードを返す
  • http_request_denied: HTTPリクエストを拒否し、HTTPリクエストはフォワードしなかった。
  • http_upgrade_failed: プロキシとネクストホップ間でHTTP Upgradeに失敗した(502)
  • proxy_internal_error: オリジンと関係しないプロキシ内のエラー(500)

Fake SNIという提案仕様について

SNIを用いた通信のブロッキング及び、「Encrypted SNI拡張」のブロッキングについてはIETFTLS WGでも話題となりました((TLS) SK filtering on SNI, blocking ESNI)。

Encrypted SNIはSNIを暗号化する一方で、ClientHelloにencrypted_server_name拡張をつけます。そのため、経路上の観測者はEncrypted SNIが使われていることを検知できます。

それを回避するために、ニセのSNIをつける「Fake Server Name Indication」というdraftが提出されています。

初版のdraftであり、これから議論のあるところだとは思うが、とりあえず面白そうなので読んでみる。

Fake SNI

Fake SNIを利用するサービスは事前に、偽のホスト名を公表します。DNSを利用することを想定していますが、他の方法でも問題ありません。

DNSを用いる場合は、TXTレコードで下記のように本来のドメイン名に関連付ける偽のホスト名を定義しておきます。

_fakesni.example.com. 60S IN TXT "myfakerecord.com IP"

f:id:ASnoKaze:20190220124940p:plain

  • まずクライアントは通信を行うドメインの偽のホスト名を取得します。
  • クライアントは取得した偽のSNIを設定してTLSハンドシェイクを開始します
  • サーバは、Fake SNIのSNIを受け取った場合に、本来のホスト名の証明書を返します。なおTLS1.3ではCertificateは暗号化される
  • クライアントは本来のホスト名の証明書が取得でき、正しく検証できた場合に通信を継続します。

こうすることで、経路上の観測者にはFake SNI使ってるかどうか区別はつかなくなります。

感想

偽のホスト名は公開されているため、経路上の観測者自身が名前解決を行い記録しておいたり、DNS通信を収集することで、偽のホスト名と紐づく本来のドメインはわかりうる。頻繁に偽のホスト名を変えることで対応はできそうではあるが

また、偽のホスト名として任意のドメインを使えるというのは、経路上の中間装置などにどのような影響を与えうるか気になった。