QUIC for SSH の提案仕様が出たよ

QUIC-based UDP Transport for SSH」という提案が提出されています。

トランスポートプロトコルとしてQUICを利用することで、様々な恩恵を受けることが出来ます。

  • ユーザランドでコネクションが管理されるため、TCPとは異なりOSレイヤのでコネクション切断の影響をうけない
  • IPアドレスが変わっても接続を維持できる(コネクションマイグレーション)
  • 経路上の第三者による切断に耐性がある(QUICでは通信の切断にも鍵が必要)

個人的にも、SSHがQUIC上で動作することで切断しづらくなることを期待しております。

それでは、この仕様についてざっと見ていくことにしましょう。

ただ、まだまだこれから議論がされる提案仕様ですので、設計は大きく変わるでしょう。

QUIC-based UDP Transport for SSH の概要

QUICは内部的にTLSハンドシェイクを行ってコネクションの確立を行うため、SSHの鍵交換の仕組みをそのまま入れ込むことが出来ません。ですので、「QUIC-based UDP Transport for SSH」では、鍵交換だけSSH流のやりかたをし、その鍵を用いてQUICのハンドシェイクが完了した状態からQUICを始めるという少々トリッキーな仕組みになっています。

(なので、SSH over QUICというより、仕様のタイトル通りQUIC for SSHという感じですね)

この仕様では、コネクションを確立するために必要な情報を交換する「SSH_QUIC_INIT」「SSH_QUIC_REPLY 」というメッセージと、通信のキャンセルをする「SSH_QUIC_CANCEL」というメッセージを新しく定義しています。

f:id:ASnoKaze:20200709010023p:plain

SSH_QUIC_INIT」および「SSH_QUIC_REPLY 」は、SSH関連の情報とQUIC関連の情報が含まれます。

SSH関連の情報として、署名アルゴリズム、信用する鍵のフィンガープリント、ECDH用のパラメータ(SSH_MSG_KEX_ECDH_INIT)などが含まれます。

QUIC関連の情報としては、QUICのハンドシェイクが完了した状態とするために、QUICバージョンや暗号スイートが含まれます。

QUICコネクションが確立されると、ストリームID 0上でデータがやりとりされます。

SSH_QUIC_INIT

SSH_QUIC_INITが持つ情報について詳しく見ていきます

  • client-connection-id: QUICのコネクションIDとして使用される
  • client-quic-versions: サポートするQUICバージョンの列挙
  • client-sig-algs: クライアントの署名アルゴリズム
  • trusted-fingerprint: クライアントが信頼する鍵のフィンガープリント
  • client-kex-alg-name: クライアントが対応する鍵交換アルゴリズム
  • client-kex-alg-data: 鍵交換のパラメータ
  • quic-tls-cipher-suite: QUICの暗号スイート対応リスト
  • ext-pair: 拡張領域
  • padding: アンプ攻撃対策のパディング

各種、使用するアルゴリズムは既存のSSHで定義されたもの、暗号スイートはTLSで定義されたものをそのまま使う

SSH_QUIC_REPLY

SSH_QUIC_REPLYが持つ情報について詳しく見ていきます

  • server-connection-id: QUICのコネクションIDとして使用される
  • server-quic-versions: 選択したQUICバージョン
  • server-sig-algs: サーバの署名アルゴリズム
  • server-kex-algs: 選択した鍵交換アルゴリズム
  • quic-tls-cipher-suite: 選択したQUICの暗号スイート
  • ext-pair: 拡張領域
  • server-kex-alg-data: 鍵交換のパラメータ

各種、使用するアルゴリズムは既存のSSHで定義されたもの、暗号スイートはTLSで定義されたものをそのまま使う。

なお、受け取ったSSH_QUIC_INITでは処理ができない場合は、エラーを返すことになる。具体的にはserver-connection-id, server-kex-alg-dataに空を設定し、拡張領域にerr-descというフィールドを設定いしエラー理由を通知する。

QUICコネクションのセットアップ

QUICコネクションについては、先の流れで得られた情報を元にハンドシェイクが完了した状態となります

  • QUICバージョンは選択された通り
  • TLS暗号スイートは選択されたとおり
  • QUICの鍵フェーズは0
  • シェアードシークレットは、SSHの鍵交換されたものから導出される

その他

QUIC Transportパラメータの交換どうするんだ、、?

個人的にはやはり通常通りのQUICハンドシェイクをしたほうが良いのではと思うが、鍵交換部分はどうするのがいいのだろう、、、SSHの鍵交換してTLS PSKに注入するような感じのことが出来る?

HTTPと硬直化 (ossification) の問題

「硬直化(ossification)」はあまり聞き慣れない言葉だが、インターネットやWebの通信において問題となってきています。

新しい機能の展開を阻害するこの問題は、HTTPにおけても問題になっておりましたが、HTTPの標準化を行うIETFで動きがありました。

IETFのHTTP WGではオープンレターとして「HTTP and Web Application Firewalls: Managing Ossification Risk」を公開し、WAFベンダと連携してこの問題に取り組んでいく意思が示されています。

この事に関して簡単に説明していきます

目次

硬直化(ossification) とは

「硬直化(ossification)」とは、インターネット上のアプリケーションやネットワーク装置などによってプロトコルの新しいバージョンや新しい拡張仕様の通信が正しく中継されず阻害されることです。ここでいうネットワーク装置は L3~L7で、たとえばロードバランサやプロキシ、ファイアフォール、WAFなどのセキュリティ製品などなどです。

実際、プロトコルとして拡張が許可されているはずなのに、不正な挙動となり通信を阻害してしまう環境がみつかります。TLS1.3やTCP Fast Openといったプロトコルは、標準課程の中で正しく通信が中継されない(フォールバックされない)環境などが見つかりました。

詳細は下記を参考に

HTTPにおける 硬直化(ossification)

HTTPもプロトコルの改善・拡張が日々行われております。わかりやすいところで、HTTPヘッダはベンダー独自にも、標準仕様としても新しいものが増えております。

そのため、HTTPにおいても硬直化(ossification)の問題は発生しており、新しいヘッダがWAFなどによりブロックされるなどの例が知られています。

  • Sec-Metadataヘッダでヘッダ値として「target=」を含めたらファイアフォールによってブロックされた (Issueリンク)
  • Cache-Controlで許可された値以外をブロックするという、OWASPでのルール (リンク)
  • Structured Field Valuesという仕様に基づいて、ヘッダ値に記号文字を入れたところ正しく通信できなかった (リンク)

このように、新しく拡張しようとして正しく機能しない部分は"錆びついた"とも表現されます。そのために、拡張性が維持できるように、錆びつかないようにグリス(GREASE)する試みが進んでいます。

グリス (GREASE)の例

TLS1.3のデプロイ時に発見された拡張性が正しく動作しない問題に対して、GREASE(RFC8701)という仕組みが考案されました。実装がサポートしてない未知の拡張仕様が正しく無視されるように、未知の予約された拡張番号を送ります。

例えば、cipher suitesおよびALPN識別子として下記の値を送信します(別のフィールドには別のGrease用の値が予約されています)

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

こうすることで、この未知の値を受け取った実装が正しく無視することを確認できます。また、通信が阻害されてしまう場合は、その事に気づくことができ、修正するように促すことが出来ます。このように、拡張性が錆びつかないようにグリスするということです。

HTTPにおけるGREASE

先に述べたように、HTTPでも硬直化(ossification)が問題になっています。HTTPもGREASEしようというながれになりました。HTTPの拡張性を維持するため、メッセージを受け取った相手が未知の値を正しく無視し通信を阻害しないことを確認できるようにします。

HTTP/2のGREASEについては以前紹介したとおりです。
asnokaze.hatenablog.com

今回IETFのHTTP WGから、「HTTP and Web Application Firewalls: Managing Ossification Risk」というオープンレターと「Greasing HTTP」という提案仕様が提出されています。

HTTP and Web Application Firewalls: Managing Ossification Risk」はWAFベンダーに対して、プロトコルの発展と硬直化(ossification)の問題を説明しています。セキュリティ上、未知の機能をブロックしたいということに関しても理解を示した上で、そのうえで、Greaseのために議論の場(メーリングリスト)を用意し、協力していく意思を示しています。

Greasing HTTP」という提案では、新しい拡張について適切な通知期間を設け、その後デプロイ出来るようにするというGreasingのプロセスについて述べています。

例えば、Greasing送信者、受信者、調停者を設け、適切なGREASE(ヘッダ名・ヘッダ値)を十分な量流すといったようなことが述べられています。

まだまだ提案ですのでこれから動きがあるところだと思います。

TLSハンドシェイク中にアプリケーション設定を送信可能にする提案仕様 (TLS ALPS)

2020/07/22 追記
HTTPでTLS ALPSを利用方法の仕様が別途提出されています。
Using TLS Application-Layer Protocol Settings (ALPS) in HTTP


GoogleのVictor Vasiliev氏から「TLS Application-Layer Protocol Settings Extension」という提案仕様が出ています。

これは、アプリケーションプロトコルで必要なパラメータをTLSハンドシェイク中に送信しちゃおうという提案仕様です。

例えば、HTTP/2やHTTP/3では、TLSハンドシェイク直後にSETTINGSパラメータを送り合い、お互いに受信できるヘッダ最大長やヘッダ圧縮に関するパラメータなどを相手に通知します。最初のHTTPリクエストをより早く送るために、相手からSETTINGSパラメータを受信する前にHTTPリクエストを送信は可能ですが、相手の上限値などを知らずに送信を開始する形になります。

このようなアプリケーションプロトコルで必要となるパラメータをTLSハンドシェイク中に相手に通知してしまおうという話です。(今回はHTTPの例を出しましたが、この拡張仕様はアプリケーションプロトコルを限定していません)

なお、送られるアプリケーションパラメータはEncryptedExtensionsで送られるため暗号化されます。

alps

この仕様では、application_settings (alps) TLS拡張を新しく定義しています。

流れとしては

  • クライアントは、ClientHelloでalps拡張を送信します。これは単純に、この仕様に対応していることを示すために送られます。(alpnで送ったプロトコル識別子のうち、どれでサポートしてるかを示す)
  • サーバは、この仕様に対応していればServerHelloで実際にアプリケーションパラメータを含むalps拡張を送信します。
  • (クライアント認証を行う場合は、クライアントはアプリケーションパラメータを含むClientApplicationSettingsメッセージを送信できます)

f:id:ASnoKaze:20200628014252p:plain

補足

HTTPを含むアプリケーションプロトコルは、クライアントが先にアプリケーションデータを送信します。それまでに、サーバ側が設定したいパラメータがクライアントに届いてる必要があります。単純にこれを満たすためのであれば、既存のTLS1.3でも可能です。

サーバはFinishedを送ったあとに続けてアプリケーションデータを送信できるため、サーバからクライアントにアプリケーションパラメータを送信できます。

この点については、draft中に以下の通り書かれています

  • 多くの実装は、クライアントからFinishedを受け取るまでアプリケーションデータの送信を待ちます

その他にも、0-RTTハンドシェイク時に以前のアプリケーションパラメータをTLSレイヤで保存しておけるといった利点があります。

なるほど。

CookieのSameSite属性にFirstPartyLaxを追加する提案仕様

2020/11/02 追記
First-Party Sets自体はChromeへの実装が進められる一方、FirstPartyLaxについては廃止されたようです


Cookieのセキュリティ改善を推し進めているGoogleのMike West氏から「First-Party Sets and SameSite Cookies」という提案仕様がIETFで提出されています。

この提案仕様では、CookieのSameSite属性に下記の2つを指定できるようにします。

  • FirstPartyLax
  • FirstPartyStrict

これにより、Cookie送信先をFirst-Partyに緩和することが出来ます。

既存のDomain属性では一方的に共有先に追加していましたが、後述のFirst-Partyはお互いのドメインがFirst-Partyとして認識しあってる点が異なっています。

これらの説明をする前にドメインを超えてFirst Partyを指定できる「First-Party Sets」という仕様を紹介します。

First-Party Sets

First-Party Sets」は同じくMike West氏が提出している仕様ですが、こちらはW3CのWICGで議論されています。

例えば、Webでは、全く異なるドメインが同一主体によって運営されているものがあります。

こういった、ドメインが "First Party" であるというのを分かるようにするのがFirst-Party Setsです。この仕様では、あくまで"First Party"が分かるようにするだけで、既存のWebの仕組みや機能を変更することは有りません。

またFirst-Party Setsの仕様では、無関係なサイト間でのログインや、無関係なサイト間でのターゲティング広告の効果測定は目的に含まれていません。

First-Partyの宣言の仕方

下記ドメインがFirst-Partyであることを宣言する例を示します。

f:id:ASnoKaze:20200621012935p:plain

このとき、Well-Known URLを使用します。パスは「.well-known/first-party-set」で、ここにJSONを配置します。

https://a.example/.well-known/first-party-set

{
  "owner": "a.example",
  "version": 1,
  "members": ["b.example", "c.example"],
  "assertions": { 
    "chrome-fps-v1" : "<base64 contents...>",
    "firefox-fps-v1" : "<base64 contents...>",
    "safari-fps-v1": "<base64 contents...>"
  }
}

https://b.example/.well-known/first-party-set

{ "owner": "a.example" }

お互いのドメインでお互いがFirst-Partyであることを示します。assertionsではUAのポリシーをマッチしていることを示す署名がつけられます ( Certificate-Transparencyのような、別途署名機関が想定されている)。

また、b.exampleはアクセスしてきたクライアントに「Sec-First-Party-Set」HTTPレスポンスヘッダを返すことで、ownerを示すことが出来ます。

  Sec-First-Party-Set: owner="a.example", minVersion=1

このレスポンスヘッダを受け取ったクライアントはそれぞれのjsonファイルを取得し、マッチしていれば両ドメインをFirst-Partyとして扱います

FirstPartyLax、FirstPartyStrict

First-Party Sets and SameSite Cookies」で定義されるSameSite属性のFirstPartyLaxとFirstPartyStrictは、CookieをFirstPartyには送信するように指定できるようになります。

Set-Cookie: test=value; SameSite=FirstPartyLax

Lax, Strictの意味合いは元のSameSite属性と同様、LaxではトップレベルのナビゲーションでもCookieが送信されます。

NginxのHTTP/3を試す (2020年6月)

NginxのHTTP/3対応版が公開されました。実際に動かしていきます。(なお、現在サポートしているのはHTTP/3 draft 27版です)
www.nginx.com

基本的には 「README」 の通りやるだけです。

HTTP/3の詳細についてはガッツリ解説を書いたので、ご参考にしていただければ
asnokaze.hatenablog.com

準備

(ちなみに環境は Ubuntu18.04 Bionicです)

パッケージのインストールと、依存するboringsslのビルドをしておきます
https://boringssl.googlesource.com/boringssl/+/HEAD/BUILDING.md

$ sudo apt install mercurial ninja-build
$ git clone https://boringssl.googlesource.com/boringssl
$ cd ./boring/
$ mkdir build
$ cd build
$ cmake -GNinja ..
$ ninja
$ ../

nginxのビルド

ビルドします

    $ hg clone -b quic https://hg.nginx.org/nginx-quic
    $ cd nginx-quic
    $ ./auto/configure --with-debug --with-http_v3_module       \
                       --with-cc-opt="-I../boringssl/include"   \
                       --with-ld-opt="-L../boringssl/build/ssl  \
                                      -L../boringssl/build/crypto"
   $ make
   $ #sudo make install ##面倒くなければ install してしまっても良い

設定と起動

/usr/local/nginx/conf/nginx.conf にこんな感じで入れます
listenにhttp3とreuseportを指定します。また、HTTP3対応をクライアントに通知するために、Alt-Svcヘッダを送ります。

    http {
        server {
            # for better compatibility it's recommended
            # to use the same port for quic and https
            listen 443 http3 reuseport;
            listen 443 ssl;

            ssl_certificate     certs/example.com.crt;
            ssl_certificate_key certs/example.com.key;
            ssl_protocols       TLSv1.3;

            location / {
                # required for browsers to direct them into quic port
                add_header Alt-Svc 'h3-27=":443"; ma=60';
            }
        }
    }

接続

公式曰く、Firefox 75以上、Chrome 83以上で動くらしいので

今回は試しにChrome Canaryでhttp3 draft27を有効にして起動します。

 $ ./chrome --enable-quic --quic-version=h3-27 

chromeの起動パスがわからない場合は、chromeを起動して、chrome://version/ とURLバーに打ち込むと、起動パスがわかります。それに、上記オプションを追加します。

(ブラウザの進捗は早いので、将来h3-27の代わりに仕様上最新のh3-29を使用できる可能性もあります)

動作確認

接続するとChromeデベロッパーツールからh3-27で通信していることが確認できました

https://asnokaze.com/
f:id:ASnoKaze:20200611133029p:plain

HTTP/3の解説を書いた (2020/06/01)

@flano_yukiがHTTP/3について書きました。(無料です。

2020年6月時点の内容となっています。概ねQUICやHTTP/3の大枠は固まっており、2021年の内容と照らし合わせても、大きな変更はありません。PDFを下に貼ってあります。


, (宣伝) また、2021年6月にアップデートも含め、WEB+DB PRESS Vol.123に記事を書かせていただいております(有料) gihyo.jp

http3-note

https://github.com/flano-yuki/http3-note にPDFを置きました 公開形式は、そのうちなんとかするかも

内容

  • 1. はじめに(HTTP/3と概要)
    • 1.1 はじめのはじめに
    • 1.2 HTTPのセマンティクスとバージョンの話
    • 1.3 HTTP/3の概要
    • 1.4 HTTP/3 と呼ばれるまでの道のり
    • 1.4.1 Google QUICの実験
    • 1.4.2 HTTP over QUIC、標準化の開始
    • 1.4.3 HTTP/3への改称
    • 1.5 標準化動向を追うために
  • 2 QUICについて
    • 2.1 QUIC、はじめに
    • 2.2 QUICの概要
    • 2.3 QUICコネクションとQUICパケットの基礎
    • 2.4 フレームについて
    • 2.5 ストリームについて
    • 2.6 コネクションの確立
    • 2.7 コネクションのクローズ
    • (TODO) 2.8 負荷分散・トラフィックのオペレーション
    • 2.9 その他 (FEC, Multipath, LB)
    • 2.9.1 Forward Error Correction(FEC)
    • 2.9.2 MP-QUIC
    • (TODO) 2.9.3 QUIC-LB
    • 2.10 応用例
    • TODO
  • 3 HTTP/3について
    • 3.1 HTTP/3のはじめに
    • 3.2 HTTP/3対応の通知とコネクションの開始
    • 3.3 QUICストリームの利用
    • 3.4 HTTP/3 フレーム
    • 3.5 HTTPメッセージの送受信
    • 3.5.1 HTTPメッセージを送るまで
    • 3.5.2 HTTPメッセージの送受信
    • 3.5.3 CONNECTメソッド
    • 3.6 ヘッダ圧縮 QPACK
    • 3.6.1 ハフマン符号
    • 3.6.2 静的テーブル、動的テーブル
    • 3.6.3 ヘッダ表現
    • 3.7 サーバプッシュ
    • 3.8 優先度制御
    • 3.9 コネクションの終了
    • 3.10 拡張性
  • 4 HTTP/3 拡張仕様と応用
    • 4.1 HTTP/3 拡張仕様と応用、はじめに
    • 4.2 HTTP/2の拡張フレーム
    • 4.3 DATAGRAMフレーム
    • 4.4 WebTransport
    • 4.4.1 背景と概要
    • 4.4.2 WebTransport over QUIC
    • (TODO) 4.3.3 WebTransport over HTTP/3
    • (TODO) 4.5 MASQUE
    • (TODO) 4.6 RIPT
    • (TODO) 4.7 HTTP over multicast QUIC

CONNECT-UDP HTTPメソッドの仕様 (RFC 9298)

2022年8月追記: この記事は古くなっています。仕様自体は RFC 9298として標準化さました。
RFC 9298: Proxying UDP in HTTP


2022年7月追記: この記事は古くなっています。現在の最新仕様では、拡張CONNECTメソッドを使うことになってます
asnokaze.hatenablog.com


MASQUEプロトコルの標準化の流れで、GoogleのDavid Schinazi氏から「The CONNECT-UDP HTTP Method」という提案仕様が提出されている。

MASQUEは、HTTP/3でプロキシサーバに接続した後にそのコネクションをトンネルとして使用し、VPNのように任意のデータをやりとりするプロトコルです。通信を観測する第三者からは、ただのHTTP通信を行っているようにしか見えません。

このMASQUEは現在IETFでワーキンググループを作成している最中であり、まだまだ議論が始まったばかりの仕様になります。

CONNECT-UDP HTTPメソッド

CONNECT-UDP HTTPメソッドは、HTTPの接続をUDPでプロキシするのに使用されるメソッドです。

クライアントは以下のようなリクエストを送信します (HTTP/2以降は疑似ヘッダで表現される)

:method CONNECT-UDP
:authority server.example.com:443
Datagram-Flow-Id 2

(Datagram-Flow-Idヘッダについては後述する)

このリクエストを受け取ったプロキシは接続先のサーバに対してUDPソケットを開き、クライアントに200番台のレスポンスを返す。それ以降プロキシはう受け取ったデータをUDPでターゲットサーバに転送します

なお、HTTP/3を使用している場合は「HTTP/3 DATAGRAM Frame」を使用できます。HTTP通信は同一ストリーム上ではデータは送った順番通りに処理される必要があります。HTTP/3 DATAGRAM Frameは、パケットが欠損したりパケットの順番が入れ替わっても届いた順番で処理していいデータを運ぶ用のフレームです。

以前解説した、QUICレイヤのDATAGRAM Frameとは異なり、HTTP/3レイヤで送信されるフレームです。
asnokaze.hatenablog.com

フォーマットも微妙に異なっており、Flow Identifierという識別子を持ちます。CONNECT-UDPリクエストを送信する際に、Datagram-Flow-Idヘッダを送信しますが、利用するHTTP/3 DATAGRAM FrameのFlow Identifierと同じ値を入れて、紐付けを行います。

これによりHead-of-line blockingを防ぐことができます。


そのた

(間違ってる箇所があったらすみません)