Cookieの改訂版仕様 rfc6265bis の変更点

Cookieの改訂版仕様 rfc6265bis について、その変更点をざっと眺めていく

はじめに

Cookieの仕様は『RFC 6265: HTTP State Management Mechanism』として標準化されています。

そのCookieの仕様の改訂版が『rfc6265bis』と呼ばれているもので、現在標準化作業が進められいています。"SameSite属性"や”Cookieプレフィックス”などが仕組みとして追加されているほか、仕様として曖昧だった部分が明確化されています。

これらの機能はすでにブラウザに実装されているため、今回の改訂で大きくCookieの仕組みが変わるということはありません。

そんなrfc6265bisの仕様だが、標準化作業を進めているIETF HTTPWGにおいて"Working Group Last Call"が呼びかけられました。この段階までくれば、機能追加などの大きな変更は入らないと思われます。

SameSite属性

新しく定義されたCookieの属性であり、リクエストが同じSiteの時のみCookieが送信されるように制限できる。

属性値として次の値が指定できる

  • Strict: same-site のときのみCookieが送信される
  • Lax: same-siteもしくは、トップレベルナビゲーションじのときにCookieが送信される
  • None: same-site及びcross-site の両方でCookieが送信される

今まで、一度セットしたCookieの属性をサーバ側から確認する方法はありませんでした。サーバ側からすると送られてきたCookieについて、預かり知らぬところで属性が変更されている可能性もあるわけです。

Cookieプレフィックスを付与することで、属性を保証できるようにします。

__Secureプレフィックス

Cookie名を"__Secure"から始めることで、Secure属性がついていることを保証します。
ブラウザはSecure属性のついてないCookieを拒否します。
(サーバ側からするとこのプレフィックス付くCookieが送られてきたら、Secure属性がついてると確信できる。)

ブラウザはこのCookieは拒否します。

Set-Cookie: __Secure-SID=12345; Domain=site.example
__Hostプレフィックス

Cookie名を"__Host"から始めることで、『Secure属性・Path属性==/・Domain属性無し 』であることを保証します。
ブラウザはそうではないCookieを拒否します

ブラウザは以下のようなCookieを拒否します

Set-Cookie: __Host-SID=12345
Set-Cookie: __Host-SID=12345; Secure
Set-Cookie: __Host-SID=12345; Domain=site.example
Set-Cookie: __Host-SID=12345; Domain=site.example; Path=/
Set-Cookie: __Host-SID=12345; Secure; Domain=site.example; Path=/

非セキュアなオリジンからの Secure属性の上書きを禁止

非セキュアなオリジン(例: スキームがhttp)から、secure属性がついたCookieを上書きを禁止。

例えば、パブリックなWiFi利用時に中間者攻撃にてhttp://~なURLへ誘導され、secure属性を消すような攻撃を不可能にします。

nameless cookieの許容

名前の無いCookie(nameless Cookie)は、RFC6265時点で拒否するよう指定されていますが、実際に使用されているケースが有るため、受け入れるように変更されました(ただし非推奨)。

例えば次のようなset-cookieヘッダがnameless Cookieの例になります。

set-cookie: =hoge
set-cookie: hoge

上記のように設定されたCookieは、リクエスト時に次のように付加されます

cookie: hoge


ただし、以下のような名前も値も無いset-cookieは無視されます

set-cookie: =

Cookie名、Cookie値の上限長の指定

Cookie名とCookie値の合計が4096バイトより大きい場合、ブラウザは保存処理時にそのエントリを無視する。
(以前はShouldだったが保存アルゴリズムに組み込まれた)

Expires属性の年が2桁の場合の処理の指定

Expires属性は通常 4桁で年が表記されますが、2桁も許可されています

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT

2桁の場合は、以下のように解釈するよう明示されました

  • 70~99の場合は1900年代を示す
  • 0~69の場合は2000年代を示す

Max-Age/Expires の上限

Max-Age/Expiresの上限を400日とした。

その他

その他にも細かい修正が幾つかある、rfc6265からの差分については提案仕様を参照してください。

  • Cookie2, Set-Cookie2のIANA登録を削除
  • 制御文字やタブ文字の拒否
  • ”Secureコネクション”かどうかはユーザエージェントによって判断され、localhostも含まれうる
  • Service Workerでの取り扱いの追加
今回入らなかった機能

Cookieに対する取り組みは幾つかありますが、今回の改訂には含まれていないものもあります。
幾つか紹介します

privacy.txt の提案仕様について

Webサイト利用者に関するプライバシーが重要視されるなか、プライバシーポリシーURLや各種オプトアウト用URLを提示できる『privacy.txt』の仕様がIETFに提出されている。

このprivacy.txtは、『robots.txt』『security.txt』『ads.txt』と同様に、ドメイン直下のPATH、もしくは/.well-knownのパスに配置される。

privacy.txtの中身

何が記述できるかについては『A File Format to Aid in Consumer Privacy Enforcement, Research, and Tools』仕様に書かれている

プライバシーポリシー情報

プライバシーポリシーに関するURLを提示できる

  • Privacy-policy: URL
    • プライバシーポリシーのURLを提示
  • Privacy-policy-text: URL
    • プライバシーポリシーのテキスト形式のURLを提示
各種アクション用URL情報

データ削除やオプトアウト用のURLを提示できる

  • Action-delete-account-and-data: mailto:EMAIL|URL
    • アカウントおよびデータを消すためのメール宛先・URL
  • Action-delete-personal-data: mailto:EMAIL|URL
    • 個人の情報を消すためのメール宛先・URL
  • Action-opt-out-sharing:mailto: EMAIL|URL
    • 三者との情報共有をオプトアウトするためのメール宛先・URL
  • Action-shared-list:mailto: EMAIL|URL
    • データが共有される第三者のリストを取得するメール宛先・URL
  • Action-opt-out-marketing: mailto:EMAIL|URL
Cookieに関する情報

プライバシー法で求められる、Cookieの情報を記述できるようにすることを目的とする

Cookie: FIELD#1,FIELD#2,...FIELD#7』という形式であり、各FIELD#1~7には次の情報が入る

( 必須Cookieかどうか取れるので、ポリシー見てブラウザがいい感じにやってくれたりするのだろうか... )

さいごに

今のところIETFで活発に議論されている様子はみられない、次のIETFミーティングでdispatchされるかもしれない

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)