サポートしてないwell-known URIs に対するステータスコードを確認する提案仕様

Webの仕様ではWell-know URIとして特別なPathを指定するものがあります。

例えば、現在議論が進められている「A Well-Known URL for Changing Passwords」では .well-known/change-password をパスワード変更用のページにリダイレクトする特別なパスとして定義しています。

実際にtwitterFacebookではすでに対応しています

サポートしていないwell-known URIs に対するリクエストには200番台以外のステータスコードを返さなければなりません。しかし、実際にはリソースが存在しないのに200番台のレスポンスを返すサーバがいるようです。


そのために 200番台を絶対に返さない well-known URIs を定義し、そういったサーバを検出できるようにするのが「Detecting the reliability of HTTP status codes」です

Detecting the reliability of HTTP status codes

後述の通り、「Detecting the reliability of HTTP status codes」では必ず200番台のレスポンスを返さないwell-known URIsを定義します。

具体的には下記のとおりです

.well-known/resource-that-should-not-exist-whose-status-code-should-not-be-200

このパスに対してリクエストを送ることで、サーバが返すステータスコードの信頼性を確認できます。

これらの仕様は、W3CのWICGで管理されているドキュメントです。これから、まだまだ変更される可能性があります。

Compact TLS 1.3の提案仕様

TLS1.3の仕様を書いたEric Rescorla氏らより「Compact TLS 1.3」という提案仕様が提案されており、すでにWG Draftとなっています。

このCompact TLS (cTLS)は、よりコンパクトなTLSを定義しデータの通信量を削減しています(リソースなどが制限されたデバイスでも利用できるようにというモチベーションもあります)。TLS1.3自体とisomorphic(同型)でありやり取りされるメッセージの意味自体は同じですがメッセージのフォーマットは異なっており、cTLSとTLS1.3自体と相互通信はできません。

しかし、cTLS, TLS1.3両方に対応することはできるので、将来的にそういうサーバが出てくれば問題なく通信できるようになりそうです。

アプローチ

主なアプローチは

  • TLS1.3の冗長なフィールド、廃止されたフィールドを取り除きます (TLS1.2との互換性の都合形だけ入ってたフィールドなど)
  • 可変長整数を使い、整数値のフィールドを削減します
  • 不要な値の省略
  • デフォルト値を定義可能にするテンプレート (profile)

結果として、下記の通りデータ転送量を削減できます。
f:id:ASnoKaze:20200430000115p:plain

いくつか説明していきます

冗長, 不要なフィールドの廃止

TLS1.2との互換性のために確保されてた領域をクリーンアップしています

Record Layer

         struct {
             ContentType type;
             opaque fragment[TLSPlaintext.length];
         } TLSPlaintext;

ClientHello

         struct {
             Random random;
             CipherSuite cipher_suites<1..V>;
             Extension extensions<1..V>;
         } ClientHello;

ServerHello

         struct {
             Random random;
             CipherSuite cipher_suite;
             Extension extensions<1..V>;
         } ServerHello;

テンプレート(Profile)

ProfileはJSON形式で記述され、暗号関連のパラメータ・TLS拡張・証明書などが事前定義されており。ハンドシェイク時はこれらの値を使う場合は省略されます。また、randomの長さなども短く設定することもできます。

このProfileはクライアントとサーバで事前に共有されている必要があります。(標準Profileを定義するかや、ハンドシェイク中に使用しているProfileを相手に通知する方法などは現在議論中です。)

Profile例

   {
     "version": 772,
     "cipherSuite": "TLS_AES_128_CCM_8_SHA256",
     "dhGroup": "X25519",
     "signatureAlgorithm": "ECDSA_P256_SHA256",
     "randomSize": 8,
     "finishedSize": 8,
     "clientHelloExtensions": {
       "server_name": "000e00000b6578616d706c652e636f6d",
     },
     "certificateRequestExtensions": {
       "signature_algorithms": "00020403"
     },
     "knownCertificates": {
       "61": "3082...",
       "62": "3082..."
     }
   }

Cookieのセキュリティを改善する Scheming Cookiesについて

Chromeが「Cookie の SameSite=Lax をデフォルト化」を進めたことは記憶に新しい。
asnokaze.hatenablog.com

Cookieの改善は引き続き議論されており、Cookieの扱いでスキーム(http://やhttps://)を考慮に入れることが検討されている。

Incrementally Better Cookies

Cookieのセキュリティ改善を精力的に行っているGoogleのMike West氏は、Secure属性の利用が30%、"__Secure-"プレフィックスの利用が0.18%ほどにとどまっていると述べており(リンク)、セキュリティ改善のためにCookieの扱いを段階的に変更していくことを考えている。

同氏がIETFに提出している「Incrementally Better Cookies」では、Cookieを次のように段階的に改善することを提案している。

  1. 「SameSite = Lax」をデフォルトにする
  2. 「SameSite = None」にするにはHTTPSから配信される必要がある
  3. same-siteはスキームを考慮にいれるようにする (Schemeful Same-Site)
  4. Cookieはスキームを尊重する必要がある (Scheming Cookies)
  5. 非セキュアなスキームのCookieは、セッションの最後に削除する
  6. セッションの定義を厳しくする

すでに、最初の2つは実施されている。これに続く、Schemeful Same-Siteや、Scheming Cookiesなどについて簡単に書いていく。

ただし、同氏がBlink-devで「Reducing web compatibility risk.」述べているように、昨今の状況を鑑みてWebの互換性を壊しうる変更はすぐにブラウザには入らないだろう。

Schemeful Same-Site

Same-Site の判定は、eTLD+1である。

例えば下記はsame-siteである。

また、https:// と http:// は考慮に入らないが、Schemeful Same-Siteではスキームを考慮にいれるという変更である。

Same-site Cookieの場合は、https://example.comでセットされたcookieは.http://www.example.com へのリクエストでは付加されません。逆もそうなります。

Scheming Cookies

(same-site属性なしでも) スキームを考慮するようにします。Scheme-Bound Cookies とも呼ばれます。

ブラウザがCookieを保存する際に、スキーム情報も保存します。ブラウザは今までの条件に加えスキームも一致している場合のみ、Cookieを送信します。

非セキュアなCookieの失効

次の段階として、非セキュアなスキームでセットされたCookieをセッション終了時に失効することも検討されている。

時刻同期プロトコル Roughtime の標準化

Rougtime」という時刻同期プロトコルの標準化が進められている。

すでに、GoogleやCloudflareがRoughtimeサーバを公開している

  • roughtime.cloudflare.com
  • roughtime.sandbox.google.com

もともとは、2016年頃にGoogleのAdam Langley氏が原案を公開しており、サーバの実装が進められた。その後、時刻同期プロトコルを扱うIETFのNTP WGに仕様が提出されました。そして2020年1月に、「Rougtime」としてWG Draftになり標準化が進められています。

Roughtimeプロトコルの特徴

Roughtimeは以下の特徴を持つ

  • クライアントからリクエストを送信し、サーバから署名を付けてレスポンス(時刻+証明書+署名)を返す
  • 複数サーバを利用することで、暗号的に不正なサーバを暗号的に検知できる
  • UDPTCPに対応。UDPでは、アンプ攻撃対策が入ってる。
  • サーバにおける署名処理は、複数クライアントからのリクエストを一気に署名できるようになっている
  • バッチ的に処理を行うため、高精度の時刻同期ではない

f:id:ASnoKaze:20200412230149p:plain:w480
(図はリクエスト/レスポンス内に含まれるタグを一部咲楽しています)

Rougtimeでは、クライアントからリクエストを送り、サーバから署名のついたレスポンスが返されます。サーバの署名がついてますので、クライアントはサーバを認証できます。

クライアントはリクエストにノンス(NONC)をつけて送ります。これに対してサーバ側はレスポンス(時刻+証明書+ノンスに対する署名 SIG)を返します。これにより、クライアントからのリクエストを受信したあとに、レスポンスが生成されたことを暗号的に保証することができます。

クライアントはサーバAからのレスポンスを、乱数と結合しハッシュを撮った値を付きのサーバBへのノンスとして送ります。こうすることで、「サーバAからの時刻 < サーバBからの時刻」ということことが暗号的に保証されます。こうすることで、暗号的に不正な値を返すサーバを見つけることができます。

各サーバは事前に長期鍵として公開鍵を公開している。その鍵で一時鍵を署名して、その一時鍵を用いてクライアントからのリクエストに対して署名処理をする。

例えば、Cloudflareでは長期鍵はこのように公開されています。

   address:       roughtime.cloudflare.com
   port:          2002
   long-term key: gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo=

メッセージ

リクエストおよび、レスポンスのメッセージは下記の構造を持ちます。タグというキー・バリューのペアが複数含まれます。

最初にタグの数と、メッセージの中で1~N番目のタグがどこから始まるか示すオフセット、32bitのタグ(ASCIIでNONCやVERのように表現されます)およびタグの値が繰り返されます。

f:id:ASnoKaze:20200412231215p:plain

リクエス

リクエストは以下のタグを持ちます

  • NONC: ノンス
  • VER: プロトコルバージョン
  • PAD: UDPの場合は、アンプ攻撃を防ぐためリクエストメッセージが1024バイトを超えるようにパディングをします
レスポンス

レスポンスには、時刻を示すMIDP、証明書を示すCERT、署名値SIGとマークルツリー(後述) ROOT INDEX PATHなどである。

署名対象はSREPであり、ここに時刻やマークルツリーのROOTの値の値が含まれている。複数のリクエストを一度に処理する場合、このPREPが同じ値になる。

Response

  • SREP
    • ROOT: リクエストとの署名値を含むマークルツリーのルート値
    • MIDP: 処理の瞬間のタイムスタンプ
    • RADI: MIDPの制度を示す値 MIDP - RADI < MIDP < MIDP + RADI になる。
    • NONC: リクエストのノンス
    • LEAP: うるう秒情報
  • SIG: 署名 (signature contextとSREPかDELEを結合したものを、署名したもの)
  • CERT: サーバーの長期鍵で署名された公開鍵証明書
  • DELE: サーバーがSREPタグに署名するために使用する委任された公開鍵証明書が含まれています。
  • MINT: PUBKが信頼される最低時間(MIDP)
  • MAXT: PUBKが新楽される最高時間(MIDP)
  • PUBK: SREPタグの署名に使用される一時鍵
  • INDEX: マークルツリー内のNONCの位置
  • PATH: マークルツリー内のNONCからROOTへのパス (各ノードの32バイトハッシュ値の羅列)
  • VER: バージョン

署名のバッチ処理

Routhtimeでは、サーバは複数のリクエストのノンスに対して一度に署名処理を行います。これは、先述のマークルツリーを利用します。

ツリーの各リーフには、複数のクライアントから送られてきたノンス(NONC)が含まれます。各リーフは左から順にindexを持ちます。

親は、左のノードと右のノードを結合しハッシュを取った値が含まれています。

順々にハッシュを取ってルートが計算されると、ルートの値と時刻情報を合わせて署名を行います。その署名結果を複数のリクエストに対するレスポンスに含めます。

f:id:ASnoKaze:20200412235758p:plain

クライアントは署名値を検証するのにマークルツリー全体の情報は必要なく、自身の送ったノンス(NONC)のindex, ROOT, およびPATH(ROOTまでハッシュを計算するのに必要なノードの値)のみが必要です。

例えば、index3のPATHは赤く囲ったノードになります。

TLS Encrypted Client Hello の暗号化(ECH)に関するメモ

2020/07/19 追記

最新版では仕様名が encrypting the entire ClientHello (ECHO)から、TLS Encrypted Client Hello(ECH) に変更されました


広域盗聴行為(RFC7258)への対策として、通信の暗号化が行われています。

TLS1.3からは、サーバ側証明書は暗号化されるようになり、観測者が得られる情報はIPアドレスやSNIから得られるホスト名などになりました。

このSNIの情報も暗号化するために、さまざな議論が行われました。その中で出てきた提案として「Encrypted Server Name Indication for TLS 1.3」という提案仕様があります。

この仕様はすでに、TLS WGでWG Draftとなっており第6版まで出てます。版が進む中で幾つかの変更があり、HTTPSSVCレコードの利用や、encrypting the entire ClientHello (ECHO)といった仕組みなどが入っています。

簡単に現在のDraftをつまみ食いする。

なお、HTTPSSVCについては以前書いたとおり。こちらは、Chromeなどで対応が進んでいる。
HTTPSで接続するための追加情報を格納するHTTPSSVCレコード - ASnoKaze blog

構成

本仕様では、以下の2種類の構成が考えられている。

それぞれドメイン名として、経路上からはSNIとしてpublic.example.comが設定されているように見える。 暗号化されたSNIとしてprivate.example.orgが送られる。そんなイメージである。

f:id:ASnoKaze:20200409024404p:plain

それぞれが同居しているShared Modeと、分離しているSplit Modeの2つの構成です。

public.example.comの部分が、大手検索会社や大手CDN会社のドメイン名になり、多くのサイトの前段となれば、パプリックなドメインとプライベートなドメインを紐付けることは困難となる。

ECHO

Draft 6から、ECHOという仕組みになりました。これは、ClientHelloをまるごと暗号化する仕組みです。これによってALPNなどの領域も暗号化されるようになります。このClientHelloの暗号化は、暗号化したClientHello(ClientHelloInner)を含むClientHello(ClientHelloOuter)で送る形で転送されます。

f:id:ASnoKaze:20200409020447p:plain

ClientHelloOuterはちゃんとしたClientHelloですが、TLS拡張としてencrypted_client_hello拡張を持ちます。

encrypted_client_hello拡張には、暗号化されたClientHello及びそれを復号するの必要な情報が格納されています。

サーバは、ClientHelloInnerを復号し受理するか、もしくはClientHelloOuterを使用できます。サーバによってClientHelloOuterが選択され処理された場合、ClientHelloOuterに対するサーバ証明書がクライアントに送信されます、後述のpublic_nameとしてvalidな証明書か検証する必要があります。

暗号化

ClientHelloの暗号化方法についても簡単に触れます。

クライアントがClientHelloを暗号化するために、ECHOConfigというメタ情報を使用します。このECHOConfigは任意の方法でクライアントに伝えられますが、例えばDNSのHTTPSSVCレコードを用いる事が言及されています。複数のECHOConfigをクライアントに渡し、使用できる設定が使用されます。

    struct {
        opaque public_name<1..2^16-1>;

        HpkePublicKey public_key;
        HkpeKemId kem_id;
        CipherSuite cipher_suites<2..2^16-2>;

        uint16 maximum_name_length;
        Extension extensions<0..2^16-1>;
    } ECHOConfigContents;

    struct {
        uint16 version;
        uint16 length;
        select (ECHOConfig.version) {
          case 0xff03: ECHOConfigContents;
        }
    } ECHOConfig;

ECHOConfigは、version, length, およびECHOConfigContentsからなります。versionは0xff03が使用されます。ECHOConfigContentsに実際に暗号化するための鍵とサイファースイートなどが入っています。

暗号化に関しては、HPKEとあるようにIETFのCFRGで議論されている「Hybrid Public Key Encryption」という仕組みが用いられている。

ということで、ECHOConfigからゴニョゴニョされて暗号化されたClientHelloが導出される。。。

pkR = HPKE.KEM.Unmarshal(ECHOConfig.public_key)
enc, context = SetupBaseS(pkR, "tls13-echo")
echo_nonce = context.Export("tls13-echo-nonce", 16)
echo_hrr_key = context.Export("tls13-echo-hrr-key", 16)
encrypted_ch = context.Seal("", ClientHelloInner)

その他

ちょっと調べきれてないトピックとして、以下がある。引き続きお勉強します

  • ClientHelloInnerが持つecho_nonce拡張
  • HelloRetryRequest
  • HPKE

「Advisory Content-Length for HTTP」と自転車置場の話し

プロトコルの標準化の場では、自転車置き場(Bikeshed)の話がたまに出てきます。そのやり取りが面白かったので、今回新しく出た提案仕様自体の解説にあわせて紹介します。

Advisory Content-Length for HTTP

IETFでHTTP WGの議長も務める Mark Nottingham氏から「Advisory Content-Length for HTTP」という提案仕様が提出されています。

ここで背景としている問題は次のとおりである。HTTPにおけるContent-Lengthヘッダーは、2つの意味で使用されています。1つめは、HTTP/1.1におけるメッセージの終端を示すため。2つめは、メッセージボディ(内容)の長さを示すために使用されます。

HTTP/1.1に置いてメッセージの終端を示すのに使用されていますが、Transfer-Encodingヘッダと合わせて使用しプロキシとバックエンドのサーバで終端の解釈を異なるようにする攻撃があったりします (HTTP Request Smuggling攻撃)。

HTTP/2では、DATAフレーム長を持ってしてメッセージの終端を示すため、Content-Lengthは終端のためには使用されません。ただし、HTTPボディの中身の長さはContent-Lengthと一致していることが要求されます。

このように、プロトコルバージョンや場所によって使い方や解釈が異なる上に、実装にも揺れがあったりします。

そこで、メッセージの終端のためではなく、コンテンツの長さを示す新しいHTTPヘッダを定義しようというのがこの提案仕様です。

初版となる現在のdraft 00では、Bikeshed-Length という名前で仮の名前で定義しています。(ヘッダ値はStructured Field Values for HTTPの定義に則る)

Bikeshed-Length = sh-item

ここで、Bikeshedという単語が出てきました。

自転車置き場(Bikeshed)

自転車置き場(Bikeshed)の解説はこちらのサイトが詳しいです。
0xcc.net

かいつまんで簡単に説明すると、誰しもが口を出しやすい議題、つまり自転車置き場の色をどうするかといった議論です。このような議論は、難しい議論や、重要な議論よりも紛糾してしまうという話です。

そのような紛糾を避けるために、「これはbikeshedの議論だね」とそのような紛糾を諌め、本質的な議論に目を目けるという事がよくあります。

プロトコルの標準化では、やはりパラメータ名・ヘッダ名がこれらに該当しがちです。そのため、提案者であるMark Nottingham氏は、ヘッダ名の議論は置いておいて、この提案の内容の話に集中するためにBikeshed-Lengthと記述したのでしょう。

反応

このBikeshed-Lengthに対して他の人の反応も面白かったので紹介します。

QUIC WGの議長を務めるLucas Pardue氏は次のようにツイートしています
「自転車置き場の色を考慮する必要があるだけでなく、長さにわたって熟考しています」(Google翻訳, tweet url)


また、この自転車置き場(Bikeshed)というフレーズを最初に使ったPoul-Henning Kamp氏もコメントしています
「私は提案されたヘッダー名は完全に素晴らしいと思うし、それを変更する理由はないと思う:-)」(Google翻訳, 投稿URL)

WebTransport over HTTP/2 の仕様について

目次

背景と仕様

以前取り上げたように、WebSocketに変わるWebTransportという新しい双方向メッセージングプロトコルの標準化および実装が進められている。
asnokaze.hatenablog.com

JavaScript APIに関してはW3C側で(仕様リンク)で策定されているが、プロトコルIETFで策定が進められている。

使用するトランスポートプロトコルによって仕様が分かれており、以下の3つが存在する。

このうち、QUICやHTTP/3が使用できない場合のフォールバック先として用意される「WebTransport over HTTP/2」の仕様が先日投稿された。

提出された「WebTransport using HTTP/2」の著者はFacebookAlan Frindell氏の他、GoogleのVictor Vasiliev氏、AppleのTommy Pauly氏らが名を連ねている。

彼らは元々 HTTP/2上で任意の双方向メッセージを行うための提案仕様を書いていたので、WebTransport over HTTP/2へと向かうのも頷ける。

WebTransport over HTTP/2 (Http2Transport)

特徴

現在の仕様では、WebTransport over HTTP/2は以下の機能を持つと述べています

  • HTTP/2上でWebTransportセッションが確立したあと、クライアントとサーバのどちらからでもWebTransport用ストリームをオープンできる。
  • WebTransport用ストリームでヘッダー圧縮、優先順位付け、フロー制御 の機能が利用できる
  • 中継者がいてもWebTransport用ストリームのデータを正しい相手に配送できる
  • WebTransport用ストリームをグループ化できる

WebSocket over HTTP/2とは異なり、セッションが確立したあとにサーバ側からWebTransport用ストリームをオープンできる。そのデータをただしくクライアントに配送するための工夫が入っている。

ただ、WebTransportでは優先度処理がどうなるかはまだ変わるところがあると思われる(WICG/Web-Transport)

概要

WebTransport over HTTP/2の仕様では、以下について言及している

  • WebTransport over HTTP/2のネゴシエーション方法 (SETTINGSパラメータ)
  • WebTransport Connect StreamsとWebTransport Streams
  • WTHEADERS拡張フレーム

それぞれ説明していく。最後に通信例を示す。

WebTransport over HTTP/2のネゴシエーション

WebTransport over HTTP/2を使うために、以下の2つのSETTINGSパラメータを投げます。

  • SETTINGS_ENABLE_CONNECT_PROTOCOL
  • SETTINGS_ENABLE_WEBTRANSPORT

それぞれ 1 の場合に、有効であることを示します。クライアントからSETTINGSフレームを送信し、サーバからも同様に有効であることを示すSETTINGSパラメータを受け取った場合のみWebTransport over HTTP/2の機能が使用できます。

WebTransport Connect StreamsとWebTransport Streams

WebTransport over HTTP/2では2種類のストリームを使用します。それぞれストリームですので、ストリームIDを持ちます。

  • WebTransport Connect Streams: クライアントからCONNECTメソッドのHEADERSフレームで開かれるストリーム。グループ化のために使用される
  • WebTransport Streams: WTHEADERSフレームを使用することでクライアント/サーバのどちらからでも開くことができるストリーム。実際にデータをやりとりするのに使用される。WebTransport Connect Streamsに関連付けられる。

実際にデータをやり取りするWebTransport Streamsは、必ずWebTransport Connect Streamsに関連付けられます。WebTransport Connect Streamsがクローズすると、関連するWebTransport Streamsもクローズされます。一つのWebTransport Connect Streamsに複数のWebTransport Streamsを関連付けることもできます。

中継者(Proxy)がいると、サーバ側からオープンしたWebTransport Streamsのデータを、どのクライアントに配送すべきか判断するためこのような仕組みになっています。WebTransport Streamsを、クライアントからオープンしたWebTransport Connect Streamsに必ず関連付けることで、その配送が分かります。

ちょっと仕組みが分かりづらいですが、後述の通信例を見るとわかりやすいかと思います。

WTHEADERSフレーム

WTHEADERSフレームは、先述のWebTransport Streamsをオープンするのに使用する拡張フレームです。クライアントおよびサーバのどちらからでも送信することができます。

HTTP/2のHEADERSフレームと似ていますが、WebTransport Connect Streamsと関連付けるために Connect Stream ID というフィールドを持ちます。その他は、HEADERSフレームと同様です。
f:id:ASnoKaze:20200311012400p:plain

ただし、優先度の依存は同じWebTransport Connect Streamsに関連付けられたストリーム間でしか考慮されません。

CONTINUATIONフレームが続く可能性があるのも同様です。

通信例

ネゴシエーション

f:id:ASnoKaze:20200311013359p:plain

まず、クライアントとサーバはSETTINGSフレームでお互いにWebTransport over HTTP/2が有効なことを確認します。

その後、クライアントからCONNECTメソッドのHEADERSフレームを送信します。このストリームがWebTransport Connect Streamsです。このとき、:protocol疑似ヘッダにwebtransportを指定します。

:status 200が返ってきたらってきたらWebTransport Connect Streamsの準備はOKです。

WebTransport Streams

WebTransport Connect Streamsの準備ができたら、WTHEADERSフレームを用いてデータ通信用のWebTransport Streamsを開きます。下記は、サーバ側からWebTransport Streamsを開く例です。CONNECT_STREAMとして、先に開いたWebTransport Connect StreamsのストリームIDを指定しています。

相手から同様にWTHEADERSフレームで:status 200が返ってきたら、DATAフレームによる双方向通信ができるようになります。

f:id:ASnoKaze:20200311014213p:plain

中間装置(Proxy)の例

中間装置(Proxy)がいる構成における、配送について簡単に補足します。
WebTransport Connect Streamsが赤、WebTransport Streams青です。

f:id:ASnoKaze:20200311014913p:plain

中間装置とサーバは一つのコネクションに集約することができますす。

先述の通り、WebTransport Streamsはサーバからオープンできます。中間装置(Proxy)は、サーバからオープンしたストリームのデータをどのクライアントに配送すべきかマッピングできる必要があります。

WebTransport Streamsは必ずWebTransport Connect Streamsと関連付けられているため、そのWebTransport Connect Streamsをオープンしたクライアントにデータを配送することができます。

その他

CORSのように、Originヘッダを使って正規のオリジンからの通信のみを許可するようですが、仕様中の例には含まれてない?