QUICにおける、明示的な輻輳シグナルを受け取る ECN対応

RFC 9000 QUICには、経路上のスイッチから明示的な輻輳シグナルを受け取る ECN (Explicit Congestion Notification) という機能に対応しています。

Googleではサーバ側はQUIC ECN対応がデプロイされており、Chromeでの実装も進められている事が報告されています。
mailarchive.ietf.org

概要

  • ECNに対応したネットワーク機器は、輻輳が起こると該当のパケットにおいて、IPヘッダのTOS fieldのCE(Congestion Experience)ビットをセットする
  • 受信したIPヘッダのCE(Congestion Experience)ビットがセットされている場合、パケットの送信者にフィードバックするため、QUICのACK-ECNでそれを通知する
  • ACK-ECNを受け取ったエンドポイントは輻輳が起こっている事を把握し、送信量を下げる
IPヘッダの CT, CE ビット

RFC2481において、IPヘッダのTOSフィールドをECN用に 2ビット使用します

  • 00: ECN非対応
  • 01, 10 : ECNに対応している事を示すのに使用します(それぞれECT1, ECT0と呼ばれる)
  • 11: CEと呼ばれ、 輻輳が起こっている事を示すのに使用します


QUICのACK ECN

QUICではACKフレームにECN Countsの情報を格納します。ECN情報を持つACKは、フレームタイプとして0x03を使用します。

ECN CountsとしてECT, CEの受信数をピアに通知します

Example

最初に述べた通り、Google, YoutubeではすでにECNに対応しているため、ACK ECNを送ってきます。WiresharkでQUICを復号すると次のようにみることが出来ます。

HTTP/3コネクション上でSSHを実行するSSH3プロトコル

IETFに『Secure shell over HTTP/3 connections』という提案仕様が提出されています。

これは、HTTP/3コネクション上でSSHを実行するプロトコルを定義しています。なお、"SSH3"という名称を仕様中で使用していますが、あくまで提案段階ですので今後変わる可能性もあります。

SSH3ではHTTP/3を使うことにより以下の特徴を持ちます

  • QUICのメリットが享受できる(例えばIPアドレスが変わってもコネクションを維持できる)
  • HTTPの認証方式をサポートする(Basic認証、OAuth 2.0、Signature HTTP Authentication Scheme
  • SSH通信の秘匿 (第三者からするとただのHTTP通信にみえる)
  • エンドポイントの秘匿 (Signature HTTP Authentication Schemeを使うことで、そこでサービスが動いてることを隠せる)
  • HTTPを使うことでロードバランサ(ホスト名やパスによるルーティング)が利用できる。
  • ポートフォワーディング時に、パケットのreordaringが不要なQUIC DATAGRAM(RFC9221)が利用できる

HTTP/3のリクエスト・レスポンスにSSHのメッセージを乗っけるわけのではなく拡張CONNECTメソッド(RFC8441)をつかって、HTTP/3のコネクションをSSH3用に利用する形になります。なお、WebTransportの利用も検討されていますが、現状はHTTP/3を利用する仕様になっています(フォールバック先としてHTTP/2を利用)。

アーキテクチャ

SSHv2は下記のとおりです


SSH3では次のとおりになります

  • 認証や、URL関連はHTTP/3レイヤで処理する
  • トランスポートはQUICが担い、チャネルの機能はQUICレイヤにマッピングされる


プロトコル

通信の開始

エンドポイントに対してQUICコネクションを確立し、HTTP/3通信を開始します。その際に、QUIC DATAGRAMおよび、HTTP/3 拡張CONNECTメソッドをSETTINGSで有効にします。

拡張CONNECTメソッドを使ってSSH3通信を開始します。この時、:protocol疑似ヘッダには"ssh3"が指定されます。パスにはURIテンプレートでユーザ名を指定できるようになっています。

https://example.org:4443/ssh3?user={username}
https://proxy.example.org:4443/ssh3{?username}

また、拡張CONNECTメソッドを送信する際にHTTPレイヤでの認証を実行します。先述の通り、認証方式としてBasic認証、OAuth 2.0、Signature HTTP Authentication Schemeなどが利用できます。

HTTP Basic認証では以下のような通信になります。

チャネル (ストリームの利用)

クライアント開始の双方向ストリームをチャネルと利用します。WebTransportと同様に、リクエストストリームとは別で新しく開始します。

双方向ストリームには最初に次のとおりに書き込まれます。

Channel {
    Signal Value (i) = 0xaf3627e6,
    Conversation ID (i),
    Channel Type Length (i)
    Channel Type (..)
    Maximum Message Size (i)
    SSH messages (..)
}

Conversation IDとして、リクエストストリームとの紐づけを行う事ができます。それ以外はSSHで必要な値ですね。
チャネルが開設したあとはSSHv2同様のメッセージを投げることが出来ます。

実装

実装も公開されている
github.com

DNSプロトコルの拡張性を担保するGreasingの提案

DNSプロトコルの拡張性を担保するために、『Greasing Protocol Extension Points in the DNS』という提案がIETFに提出されている。

これにより、オペコードやリソースレコードタイプなどにランダムな値が設定される可能性がある。

背景、Greasingについて

プロトコルの将来の拡張性を担保するために、Greasingという仕組みが導入される事があります。

実インターネットでは様々なミドルウェア実装や中継機器などが存在します。プロトコルを拡張した際、既存実装にそれらの拡張が無視される事が期待されます。ただし、実装や中継機器の予期せぬ動作を引き起こし、結果として新しいプロトコルの利用が阻害される事が知られています(ossification問題と呼ばれる)。

これは、TLS1.3策定時に、実際にインターネットでの疎通問題として注目されました。そこで、プロトコルの未知の拡張を無視するという挙動を実装に意識させるために、無視するための拡張を定義して送信するのがGreasingです。

例えば、TLSではRFC 8701でGreasing用の値が定義されており、例えばcipher suitesおよびALPN識別子に次の値を付与して良いことになっています。

  • {0x0A,0x0A}
  • {0x1A,0x1A}
  • {0x2A,0x2A}
  • {0x3A,0x3A}
  • ...

これにより、サポートしてない拡張値を無視する要件が守られていることを保つようにしています。

Greasing Protocol Extension Points in the DNS

Greasing Protocol Extension Points in the DNS』では、次の値をGreasing対象として検討している。

Greasingにランダムな値を使うか、Greasingようの値を予約するかは現在検討中になっているようです。ランダムな値を使ってしまうと、将来利用される番号と衝突してしまう場合の影響が未知数となってしまいます。一方で予約値を使うと、実装がそれを特別扱いできてしまいます。

(個人的には、TLSと同様予約値を与えるので十分なんじゃないかなとは思います)

RFC 9458 Oblivious HTTP の仕組みについて

『Oblivious HTTP』はユーザのプライバシを向上するための技術であり、各ブラウザベンダーおよびCDNベンダーが実装を行っています。

取り組みについては、幾つかの記事があがっています

今回は仕様の観点で、プロトコルの中身に触れていく

背景と目的

通信観点のプライバシーについては、通信の暗号化によりほとんどが保護されています。しかし、幾つか懸念が残っています。

  • IPアドレスは、短期的に同一ユーザを識別するのに使用できる
  • コネクションは、一連の通信が同一ユーザであることを識別するのに使用できます(HTTP/2以降複数のHTTPリクエストにコネクションを再利用する)

例えばあるサービスで、ログアウトして別アカウントとして再ログインすると、サービス側には同じ人物であることが分かってしまいます。
このようなユーザを識別する情報をさらに隠すのが『Oblivious HTTP』の目的です。

(アプリケーションレイヤなど、通信以外のトラッキング手法についてはもちろん守備範囲に入っていません。)

概要

Oblivious HTTPの全体概要は次のとおりです

登場人物
  • Client: Oblivious HTTPの通信の開始者。Target Resourceにアクセスを行いたい者。
  • Relay Resource: Oblivious HTTPの中継者。ClientのIPアドレスは分かるが、HTTPリクエストの中身は分からない。
  • Gateway Resource: Oblivious HTTPの終端者、実際のHTTPリクエストの中身を取り出すことが出来る。ClientのIPアドレスは分からない。
  • Target Resource: Gateway Resourceから実際のHTTPリクエストを受け取り、処理をして、HTTPレスポンスを返す。

ここで、Relay ResourceとGateway Resourceは独立した別の組織によって運営されていることを前提としています。それにより、Oblivious HTTPの仕組みでユーザのプライバシーが保護されます。Relay Resourceは、先述の通りCDNベンダーなどが担っている例があります。

通信の流れ
  • Clientは、あるTarget Resource宛のHTTPリクエストを暗号化するために、別経路で事前にGateway Resourceの鍵情報を取得しておきます (RFC 9458では入手方法は定義されていないが、DNS HTTPSレコードをいる手法が別RFCになっている RFC 9540)
  • Clientは、Target Resource宛のHTTPリクエストをまずバイナリ表現にエンコードします。その方法は『RFC 9292 Binary Representation of HTTP Messages』で定義されています。その後、そのデータを暗号化して、Relay ResourceにPOSTします。
POST /request.example.net/proxy HTTP/1.1
Host: proxy.example.org
Content-Type: message/ohttp-req
Content-Length: 78

<content is the Encapsulated Request above>
  • Relay Resourceは、POSTのデータをそのままGateway ResourceにPOSTする。(Relay Resourceは転送するGateway Resourceが設定されている)
POST /oblivious/request HTTP/1.1
Host: example.com
Content-Type: message/ohttp-req
Content-Length: 78

<content is the Encapsulated Request above>
  • Gateway ResourceはClientからPOSTされたデータを復号し、バイナリ表現からデコードして通常のHTTPリクエストに戻します。それをTarget Resourceに転送します。
  • Target Resourceは、HTTPリクエストを受け取り、処理をして、HTTPレスポンスを返す。

(以下逆向きに処理をしてレスポンスがClientに返る。なおContent-Typeは『message/ohttp-res』になる)

その他のトピック

幾つか仕様内にかかれているトピックを紹介する

パフォーマンス

Oblivious HTTPでは暗号化及び遅延のオーバヘッドがあります。Relay Resourceをクライアントとサーバの間、特にサーバの近くに配置することでオーバヘッドを抑えられると述べている。

リプレイ攻撃対策

Relay ResourceはHTTPリクエストの中身は見えないものの、Gateway Resourceに対してリプレイ攻撃を行うことが出来ます。
カプセル化されたHTTPリクエストの暗号化にしようされたnonceを用いることで、リクエストを一意に識別する方法はあります。

また、カプセル化されたHTTPリクエストにDateヘッダフィールドを追加することが推奨されています。これにより時間を開けて再送信されたものを検知できるようになります。

Relay Resourceの転送先について

Relay Resourceが転送先のGateway Resourceをどう選択するかは、RFC中には書かれていません。
『Oblivious Relay リソースと Oblivious Gateway リソース間の固定の 1 対 1 マッピングを前提としています』とも書かれています。

クライアントやOblivious Relayは事前知識として送信先のURLを分かっている前提になっているのかなと思いました。

また、Oblivious Relayで複数のOblivious Gatewayへの転送をサポートするために、RFC 6570 URI Templateを使うように書かれていますが具体的な使用方法は書かれてなさそうです。

暗号化まわり

カプセル化されたHTTPリクエストの暗号化にはHPKEが使用されます。詳しくはRFCを参照のこと

Key Configuration

HPKE Symmetric Algorithms {
  HPKE KDF ID (16),
  HPKE AEAD ID (16),
}

Key Config {
  Key Identifier (8),
  HPKE KEM ID (16),
  HPKE Public Key (Npk * 8),
  HPKE Symmetric Algorithms Length (16) = 4..65532,
  HPKE Symmetric Algorithms (32) ...,
}

暗号化

hdr = concat(encode(1, key_id),
             encode(2, kem_id),
             encode(2, kdf_id),
             encode(2, aead_id))
info = concat(encode_str("message/bhttp request"),
              encode(1, 0),
              hdr)
enc, sctxt = SetupBaseS(pkR, info)
ct = sctxt.Seal("", request)
enc_request = concat(hdr, enc, ct)

HTTPSレコードの使用

DNS HTTPSレコードを介しOblivious HTTPサポートを示し、Key Config配布する方法については『
RFC 9523: A Secure Selection and Filtering Mechanism for the Network Time Protocol with Khronos』というRFCで記述されています。(このRFCでもRelay Resourceの検出及び設定はスコープ外)

HTTPSレコードの例

svc.example.com. 7200  IN HTTPS 1 . ( alpn=h2 ohttp )

また、Target Resourceは/.well-known/ohttp-gateway からKey Configを提供します

『Retrofit Structured Fields for HTTP』について

IETFのHTTP WGで『Retrofit Structured Fields for HTTP』という提案仕様が出ているので簡単に紹介する

Structured Field Values for HTTP

前提にある「Structured Field Values for HTTP」についてまず触れる。

HTTPでは、HTTPヘッダ(フィールド)の値を構造化データとして扱えるようにする「RFC 8941 Structured Field Values for HTTP」という仕様があります。

その仕様では、ListやDictionaryといったタイプが定義されています。

:

Example-List: sugar, tea, rum
Example-Dict: a=?0, b, c; foo=bar

Structured Field Valuesの目的は主に2つあります

  • 新しいHTTPヘッダを定義する際につどABNFで定義を与えるのではなく、Structured Field ValuesのTypeで構文定義を与えられるようにする
  • パーサーを再利用可能にする

既存のHTTPヘッダに適応することは RFC 8941ではスコープには入っておりませんでした。既存のHTTPヘッダもStructured Field Valuesとしてパースしようというのが『Retrofit Structured Fields for HTTP』になります。

Retrofit Structured Fields for HTTP

『Retrofit Structured Fields for HTTP』では、すでに使用されているHTTPヘッダをStructured Field Valuesとしてパースすることを目的とした仕様です。

戦略は2つあります

  • そのままStructured Field Valuesとしてパースしても問題なさそうなヘッダを指定する
  • それ以外のものは、新しくStructured Field Valuesとしてマップする

具体例を見ると分かりやすいかと思います

Structured Field Valuesとしてパースする

すでに使われているHTTPヘッダでパースできそうなものは、提案仕様のなかでこのように羅列されます。

ただし注意点があり、Structured Field Valuesとして扱う上で次のことを気をつけなければなりません

  • Dictionaryのパラメータキーとして大文字・小文字を区別するケース。クオートのルール
  • 構文エラー時の挙動の違い
  • integerは15桁まで
  • 値が空文字の場合エラーとなる
  • Alt-Svc, Content-Length, Retry-After などは実装や一部互換性の無い値が知られている

Structured Field Valuesとしてマップする

すでに使われているHTTPヘッダでパースできないものは、マップした値をいれられるものとします。

例えば、Dateヘッダは

Date: Sun, 06 Nov 1994 08:49:37 GMT

DateタイプをもつSF-Dateを定義します。

SF-Date: @784111777

このように次のヘッダはマップしたものが定義されます

  • SF-Content-Location (Item)
  • SF-Cookie (List)
  • SF-Date (Item)
  • SF-ETag (Item)
  • SF-Expires (Item)
  • SF-If-Match (List)
  • SF-If-Modified-Since (Item)
  • SF-If-None-Match (List)
  • SF-If-Unmodified-Since (Item)
  • SF-Last-Modified (Item)
  • SF-Location (Item)
  • SF-Referer (Item)
  • SF-Set-Cookie (List)

『Origin-Bound One-Time Codes』の提案仕様

Apple勢から「Origin-Bound One-Time Codes」というSMSで発行するワンタイムコードのフォーマットの提案仕様がIETFに提出されています。

こちらの仕組みの標準化ということで良いかなと思います。
developer.apple.com

背景

Webのログイン時にSMSでワンタイムコードを送信し、入力させることがあります。昨今ではformの 『autocomplete="one-time-code"』属性によりユーザエージェントがSMSのコードを自動入力してくれたりします。

こちらのサイトでも書かれているように、攻撃者がフィッシングの手口で入力させたID/Passを正規サイトにインプットさせるとSMSコードを自動入力させる事ができます。
akaki.io

そこで、SMSで発行したワンタイムコードをドメインに紐付けることでこのような攻撃を防ぐのが今回の目的です

Origin-Bound One-Time Codes

Origin-Bound One-Time Codes」ではSMSやメールで発行するワンタイムコードのフォーマットを定義しています。そこにドメイン名を追加しています。

例としては次のとおりです。747723がexample.comでのみ自動入力されます。

747723 is your ExampleCo authentication code.

@example.com #747723

おまけ

AppleおよびGoogleで実装されているようです。

SMSのワンタイムコードの自動入力が幅広く使われると便利になりますね。
まだ、IETFで議論は具体的には行われてないですが、標準化まで進むと良いなーって思いました

新しいステータスコード『420 Requester Impaired』の提案仕様

新しいHTTPステータスコード"420 Requester Impaired"を定義する『An HTTP Status Code to Report Requester Impairment』という提案仕様が提出されています。

これは、送信者の障害によりサーバは処理を拒否したことを示します。

目的として、重機などの機械やAIシステムが故障してることを示唆することを掲げています。提案仕様のなかでも次のように書かれています。

ある種のAIシステムは、与えられた入力に対して幻覚を見たり、不正確な答えを返したり、異なる答えを返したりといった行動を示すことがある。このようなAIが動作するスピードを考えると、人間の要求者と同様に、AIの要求者の障害を検出することも重要である。
(DeepL)

ステータスコードの定義なのでそのままなんですが、例としては次のとおりです
(ボディは、RFC9457 形式での応答例です)

HTTP/1.1 420 Requester Impaired
Content-Type: application/problem+json
Content-Language: en
{
    "type": "https://example.com/erratic-requests",
    "title": "Requester Impaired: Erratic Behavior Detected.",
    "detail": "Potentially dangerous and erratic requests detected."
}

おまけ

著者によると、元々のアイディアでは、リクエストを送信した人間に問題があるというエイプリルフールネタとして書いていたみたいです。しかし、AIによる現実性があるとして通常の提案仕様として提出したとのことです
( https://lists.w3.org/Archives/Public/ietf-http-wg/2023OctDec/0239.html )