UUID version 6の提案仕様

[2021/04/28追記] 新しく提案仕様が出たので、新しく記事を書きました
asnokaze.hatenablog.com


先日IETFに提出された「UUID Format Update」という提案仕様の中でUUID version 6のフォーマットが言及されている。このDraftはUUIDを定義しているRFC4122を更新するものです。

ただ、まだIndividual Draftのdraft 00であり、この提案自体どうなるかは全くわからない点に注意が必要。

なお、UUIDv6自体はこの提案を行っているBrad G. Peabody氏が以前から取り組んでいた模様で、4年以上まえから案は存在していたようだ(URL)。またそのタイミングでサンプル実装も公開している。

UUIDv6

この提案仕様の中で、UUIDをデータベースのプライマリキーとして使用する際の課題をあげている。時系列順に並び替えるために他のデータが必要なことや、B-treeにデータを挿入する際に局所性がなくランダムな書き込みになってしまうことが挙げられている。
(UUIDv1はタイムスタンプが付くが、MACアドレスを使うので問題がある)

UUIDv6では、特殊な操作なく単純なソートで時系列順にでき、利用時にアクセスの局所性を持つ様になっている。(ULIDと似ている)

UUIDv6の構造

他のUUIDバージョンと同様128bitからなる。前半の64bit部分がタイムスタンプの機能、後半64bit部分がランダム性を持つことになる。

前半64bitの前方から

  • 48bit: タイムスタンプ
  • 4bit: バージョン(互換性を保つためこの位置となっている)
  • 12bit: タイムスタンプ続き

タイムスタンプ(60bit)は、RFC4122と同様「1582年10月15日(カトリック教会におけるグレゴリオ暦の実施日)0時0分からの経過時間を100ナノ秒単位で計測した数値。」(引用: wikipedia UUID)

後半64bit(既存のUUID RFC4122 と同様)

  • 16bit: clock seq
  • 48bit: node

となっている。ただし、この後半部分はアプリケーションやサーバ固有の識別子、乱数などで埋めることが許可されています。

Base64 Text / Base32 Text

(この記事では、Base64 TextとBase32 Textについての記述を追記予定です。)

終わりに

議論は古くからありますが、individual draftの初版が出たことで、それにたいするフィードバックが始まっています。
来月開催される、Dispatch WGで取り上げられるかもしれません。
https://mailarchive.ietf.org/arch/browse/ietf/?q=uuid

Retry-Afterのスコープを指定するRetry-Scopeヘッダ

一時的にサービスが使用できないことを示す503レスポンスを返す際に、Retry-Afterレスポンスヘッダを付けることで待ってほしい時間を伝えることができます。

Retry-Afterレスポンスヘッダは下記の用に秒で指定できます。

Retry-After: 120

しかし、この503ステータスやRetry-Afterヘッダは元々送ったリクエストに対してのものです。

サービスが一時的に使用できない場合は、その他のURLに対してもリクエストを送るのは待ってほしい場合が多いでしょう。そこで、Retry-Scopeヘッダという話が出てきました。

Retry-Scopeヘッダ

以前、「HTTP/1.1 (RFC 7230 〜 7235) の改訂作業がはじまる」で書いたように、HTTPセマンティクスの再改定作業が進められています。その中で、上記のようなRetry-Afterヘッダのスコープの議論がありました(Github Issue)。

この議論の後、「Retry-Scope header field」という提案仕様が書かれています(MLで共有されていますが、正式にinternet-draftとしては提出されていません)。

このRetry-Scopeヘッダでは、Retry-Afterヘッダを適応するスコープを指定します。503レスポンスヘッダを返したURL以外にも、このスコープへのリクエストを待ってもらうことができます。

Retry-Scopeは下記のように使用できます。

   Retry-Scope: /books
   Retry-Scope: https://api.example/

最後に

この議論はまだ始まったばかりですので、仕様として標準化されるかどうか、どのような形になるかはまだまだ分からないところが多いです。

RFC8701 TLS GREASEで予約される値

TLSの拡張性を維持するために、未知の拡張仕様を実装が正しく無視するように「Applying Generate Random Extensions And Sustain Extensibility (GREASE) to TLS Extensibility」という仕様が長らく議論されてきました。

2020年1月に無事RFC8701になったので、その話を簡単に紹介する。実際にいくつかの実装がすでに使っている。

詳細な背景については、大津先生の記事を参照ください
錆びついたTLSを滑らかに、GoogleによるGREASE試験 - ぼちぼち日記

なお、GREASEの仕組み自体はTLS以外にも他のプロトコルでも導入が検討されてたりします(HTTP2の例)。

GREASE

TLS1.3の標準化をすすめる中で新しいTLS拡張などを定義しました。

未知のTLS拡張は無視されなければなりませんが、正しく処理せず接続を拒否する実装などがありました。

そのため、未知のTLS拡張などを正しく無視するのを確認する用の番号群を定義するのがGREASEです。

RFC8701では ALPN, cipher suitesや、TLS拡張, namedGroups, 署名アルゴリズム, バージョン番号などにGREASE用の値が確保されます。

クライアントはそれらの値を含めてネゴシエーションしていいことになっています。ただし、その値をサーバが選んでしまった場合は切断しなければなりません。

一方、サーバはGREASE用の値を特別扱いせず未知の値として無視しなければなりません。

予約されてる値

cipher suitesおよびALPN識別子

  • {0x0A,0x0A}
  • {0x1A,0x1A}
  • {0x2A,0x2A}
  • {0x3A,0x3A}
  • {0x4A,0x4A}
  • {0x5A,0x5A}
  • {0x6A,0x6A}
  • {0x7A,0x7A}
  • {0x8A,0x8A}
  • {0x9A,0x9A}
  • {0xAA,0xAA}
  • {0xBA,0xBA}
  • {0xCA,0xCA}
  • {0xDA,0xDA}
  • {0xEA,0xEA}
  • {0xFA,0xFA}

拡張機能、namedGroups、署名アルゴリズム、およびバージョン

  • 0x0A0A
  • 0x1A1A
  • 0x2A2A
  • 0x3A3A
  • 0x4A4A
  • 0x5A5A
  • 0x6A6A
  • 0x7A7A
  • 0x8A8A
  • 0x9A9A
  • 0xAAAA
  • 0xBABA
  • 0xCACA
  • 0xDADA
  • 0xEAEA
  • 0xFAFA

PskKeyExchangeModes

  • 0x0B
  • 0x2A
  • 0x49
  • 0x68
  • 0x87
  • 0xA6
  • 0xC5
  • 0xE4

オリジン全体にポリシーを適応するOrigin PolicyをChromeで試す

目次

Chromeがオリジン全体にポリシーを適応する「Origin Policy」という仕様をサポートしてるようなので、簡単に試してみる。(現在は Chrome Canaryのみの模様)

おそらく仕様及び実装も変わりうるので注意が必要です(極力仕様の変更があったら、記事を更新するようにします)

Origin Policy

Content-Security-PolicyやFeature-Policyといった、ブラウザがページを表示する際に機能を制限したりセキュリティ上の制限を与える機能があります。これらの機能はレスポンスヘッダに該当のヘッダを付けることでポリシーを適応させることができます。

そのため、これらの機能をもれなく適応するにはレスポンスの都度きちんとこれらのヘッダーを付加しなければなりません。

また、CSPやFeature-Policyのヘッダは1キロバイトになるほど多いものもあります。HTTP/2やHTTP/3の場合はヘッダ圧縮がありますが動的ヘッダテーブルを圧迫してしまい、効率を悪くしてしまいます。

そこで、CSPやFeature-Policyのポリシーをオリジン全体に適応可能にするのがOrigin Policyです

有効にさせる方法

ブラウザにOrigin Policyを適応するために、まずSec-Origin-Policyレスポンスヘッダで適応するポリシーを通知します。なお、httpsでないとダメです。

Sec-Origin-Policy: policy=example-policy

ブラウザはこのレスポンスヘッダを見て、該当するポリシーファイルを参照しに行きます。
https://<ドメイン名>/.well-know/origin-policy/example-policy」

ポリシーファイルは以下のように、featuresとcontent_securityにそれぞれのポリシーを記述するだけです。ディレクティブ等の詳細はそちら側の仕様を参照

{
  "features": {
    "policy": "camera 'none', geolocation 'none', 'sync-xhr' 'none'"
  },
  "content_security": {
    "policies": ["script-src 'self' 'unsafe-inline', img-src 'none'"],
    "policies_report_only": ["img-src 'none'"]
  }
}

Chromiumのテストファイルを見ると例が書いてあるので参考になると思います
https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/.well-known/origin-policy/?g=0

試してみる

上記で解説したレスポンスヘッダ及びポリシーファイルを、https://asnokaze.com に設定しました。

Origin Policyによってジオロケーションへのアクセスを禁止していますので、コンソールからジオロケーションにアクセスしようとすると、ちゃんとブロックされることが確認できました。
f:id:ASnoKaze:20200126020918p:plain

なお、正しくポリシーファイルを設置しないとページの表示ができなくなってしまいます
(ページタイトルが"元のポリシーエラー"になってるのは修正中のようです)
f:id:ASnoKaze:20200126021436p:plain

https://asnokaze.com のオリジン ポリシーに沿ってブロックしました。
サイト https://asnokaze.com は、すべてのリクエストにオリジン ポリシーの適用を求めていますが、このポリシーは現在適用することができません。

WebTransport over QUICのサンプルサーバを試してみる

2022/01/04追記: この記事はすでに古いです。WebTransport over QUICは標準化せずに、WebTransport over HTTP/3を標準化する方向です。Chromeの実装もそちらに移っています。


"WebTransport over QUIC"の標準化及び、Chromiumでの実装が進んでいます。

今回はクライントを書いて、Chromiumに同梱されているquic_transport_simple_serverと通信してみました。環境は Ubuntu 18.04です。

WebTransport over QUICについては以前書いたブログを参照してください
asnokaze.hatenablog.com

quic_transport_simple_server のビルド

Chromiumのビルド手順通り勧めます (Google社員向けはスルーしましょう)
https://chromium.googlesource.com/chromium/src/+/master/docs/linux/build_instructions.md

  • Install
  • Get the code
    • Install additional build dependencies
    • Run the hooks
  • Setting up the build

フェッチに時間がかかります。
最後にビルドする際に、chromeのビルドではなく、サンプルサーバのみをビルドします

autoninja -C out/Default chrome #この部分を
ninja -C out/Default quic_transport_simple_server #こうする

証明書・鍵の作成

サーバを起動するのにPKCS#8形式の鍵が必要です

openssl req -new -key server.key > server.csr
openssl req -new -key server.key > server.csr
openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt -in server.key -out server.p8

起動

ビルドができていれば、実行ファイルがありますので実行します

~/chromium/src/out/Default$ ./quic_transport_simple_server \
    --accepted_origins="" \
    --key_file="/path_to/server.p8" \
    --certificate_file="/path_to//server.crt" \
    --allow_unknown_root_cert --port="10000" 
  • --accepted_origins: client indicationで受け付けるオリジン名(空を指定)
  • --key_file: 鍵
  • --certificate_file: 証明書
  • --port: ポート

クライアントサイド

WebTransport over QUICでは、QUIC DATAGRAMフレームのサポートが必須です。実際にはトランスポートパラメータでmax_datagram_frame_sizeを指定する必要があります。

今回はDATAGRAMフレームをサポートしてる、PythonのQUICライブラリ「aioquic」を利用します。
(exampleとして、DATAGRAM検証用プロトコル"siduck"実装が含まれてるのも選んだ理由です)

インストール方法は「aioquicのgithubリポジトリ」を参照。

./examples/siduck_client.py を雑にいじって、WebTransport over QUIC クライアントを作成します。
ソースはこちら https://gist.github.com/flano-yuki/3ab550393138066b19d2ff4d44e0cb47

~/work/aioquic$ python3 examples/quic_transport.py  localhost 10000 -k -q out -l ./ssllog
2020-01-13 21:04:51,881 INFO quic [229cca53a4662860] ALPN negotiated protocol wq-vvv-01
2020-01-13 21:04:51,882 INFO client Sent Client Indication
2020-01-13 21:04:51,883 INFO client Say Hello
2020-01-13 21:04:51,885 INFO client Echo is received: 
2020-01-13 21:04:51,885 INFO client b'hello world'

(-k はデバックログをファイルに出力、-lは復号用の鍵ファイルをファイルに出力)

quic_transport_simple_serverにデータを送って、Echoを受信することに成功しました。

修正ポイント

ポイントは

  • ALPNとして"wq-vvv-01"を指定する
  • Client Indicationを送信する(OriginとPATHをそれぞれ指定)
    • PATHとして/echoを指定すると、サーバはechoモードで動作します (ソース)
        b  = b'\x00\x00' + b'\x00\x17' + b'https://example.com:443'
        b += b'\x00\x01' + b'\x00\x05' + b'/echo'
        self._quic.send_stream_data(2, b, True)
  • クライアントからデータが送れるストリームでデータを送り、Echoされたデータを受信する
  • (いくつか試したが、DATAGARAMフレームにはEchoを返してくれない...? ソース的には未実装?)

終わりに

とりあえず、通信できるところまでうまく行ったので、ちゃんとクライアントを実装したい。quic-go もDATAGARAMフレーム実装が進んでるので、そっちでも試したい (サーバ・クライアント両方)

ダウンロードもMixed Contentsの対象となる話し

Chrome 81のデスクトップ版から、ダウンロードもMixed Contentsでブロック対象になる話があります。
これにより、https:// のページから http://でリンクされたファイルをダウロードしようとするとブラウザによってブロックされます。
chromestatus.com

詳細は「Downloads as Active Mixed Content」に書かれていますが、その他のMixed Contents同様何も表示することなくブロックされます (デベロッパーツールには表示される)。

その挙動について簡単に確認してみます。
(本日時点のChrome Canary 81.0.4023.0 で動作確認するため、挙動は変更される可能性があります)

動作確認

https://asnokaze.com/download.html にダウンロード用リンクがあります。

zip

ドキュメントによるとファイルは拡張子で判断し、実行可能ファイルなどリスクの高いものをブロックすると書いてあります。

f:id:ASnoKaze:20200111175533p:plain

https://なページから、http://なリンクをクリックしてzipファイルをダウンロードしようとするとブロックされます。見た目上は何も起こりません。
同様に、右クリックにて"名前をつけてリンク先を保存"しようとしても何も起こりません。

デベロッパーツールには以下のように、ブロックした旨ログが出ます。

Mixed Content: The site at 'https://asnokaze.com/' was loaded over a secure connection, but the file at 'http://asnokaze.com/test.zip' was redirected through an insecure connection. This file should be served over HTTPS.

もちろん、Chrome 80ですとちゃんとダウンロードできます。

画像

aリンクにdownload属性を付与した場合の動作も確認します。

<a href="http://asnokaze.com/test.png" download> http://asnokaze.com/test.png</a><br>

Mixed Contentsの場合は、新しいタブが開きダウンロードは開始されません。
もちろん、https://のリンクの場合はダウンロードが開始されます。

また、imgタグで表示されているものに関しては、右クリックでの「画像を保存」はMixed Contentsでも通常通り行えます。

HTTPメッセージに署名をするSignatureヘッダの標準化

HTTPメッセージに署名をするSignatureヘッダを定義する「Signing HTTP Messages」という仕様が提案されています。

HTTPメッセージへの署名は、様々なところで行われていますが、それぞれ独自の方式で行われています。例えば、AWS APIを叩く際に利用する「Signature Version 4 Signing」や、OAuth2.0 DPoPなどでHTTPメッセージへの署名が行われています。

その他にも、WebPackagingで使用される「Signed HTTP Exchanges」といったところでも署名が行われています。

一方でTLSではだめなのかという話もありますが、TLSを用いることでHTTPメッセージの機密性および完全性は保護されますが、TLS終端プロキシなどを経由するとそれ移行の部分で通信の完全性は担保されません。クライアントとサーバ間でHTTPメッセージの完全性を保つために、HTTPメッセージに署名する方法が必要です。

HTTPはProxyなどを通ると、viaヘッダやX-Forwarded-Forヘッダが足されたり、順序が入れわかったり、同名のヘッダが結合されたりされます。そのような場合でもちゃんと署名および検証をできるようにする標準的な署名方式を定義するのが「Signing HTTP Messages」です。

もともとは、Mark Cavage氏によって2013年頃より標準化が進められていましたが、昨年12月にその仕様を基に改めてAnnabelle Backman氏やJustin Richer氏のもと再出発となっています。同名の仕様ですので注意が必要です。背景については、[IETF106の資料]および、[MLの議論]をご覧ください

Signing HTTP Messages

Signing HTTP Messages」の仕様は主に3つの部分に別れます

  • HTTPメッセージの正規化
  • HTTPメッセージの署名および検証
  • 署名のメタデータを付与する方法

今回は正規化方法を説明し、残りふたつは合わせて説明します。

HTTPメッセージの正規化

Proxyによって、HTTPヘッダは順番が入れ替わったり、同名のヘッダが結合されたりします。

その他にも実装によって、大文字小文字の変換、HTTP/1.1とHTTP/2での変換、obs-fold(行頭空白を用いた改行)の追加・削除、などさまざまな変形が行われる可能性があります。

そのため、正規化処理を行います。

例として、obs-foldや同名のヘッダを持つ下記のHTTPレスポンスは

   HTTP/1.1 200 OK
   Server: www.example.com
   Date: Tue, 07 Jun 2014 20:51:35 GMT
   X-OWS-Header:   Leading and trailing whitespace.
   X-Obs-Fold-Header: Obsolete
       line folding.
   X-Empty-Header:
   Cache-Control: max-age=60
   Cache-Control:    must-revalidate

次のようにソート、正規化されます。
f:id:ASnoKaze:20200107005553p:plain

また、HTTPリクエストの場合はメソッドやパスも署名対象であり、リクエストターゲットも正規化されます。その例は次のとおりです。
f:id:ASnoKaze:20200107005836p:plain

HTTPボディはどうするのかというと、digestヘッダを用いてボディのダイジェストをHTTPヘッダに組み入れることでボディも合わせて署名対象に入れることができます。

Signatureヘッダ

Signatureヘッダ

   Signature: keyId="test-key-b", algorithm="rsa-sha256",
       created=1402170695, expires=1402170995,
       headers="(request-target) (created) host date cache-control
           x-emptyheader x-example",
       signature="T1l3tWH2cSP31nfuvc3nVaHQ6IAu9YLEXg2pCeEOJETXnlWbgKtBTa
           XV6LNQWtf4O42V2DZwDZbmVZ8xW3TFW80RrfrY0+fyjD4OLN7/zV6L6d2v7uB
           puWZ8QzKuHYFaRNVXgFBXN3VJnsIOUjv20pqZMKO3phLCKX2/zQzJLCBQvF/5
           UKtnJiMp1ACNhG8LF0Q0FPWfe86YZBBxqrQr5WfjMu0LOO52ZAxi9KTWSlceJ
           2U361gDb7S5Deub8MaDrjUEpluphQeo8xyvHBoNXsqeax/WaHyRYOgaW6krxE
           GVaBQAfA2czYZhEA05Tb38ahq/gwDQ1bagd9rGnCHtAg=="

それぞれのパラメータの意味は次のとおりです

  • keyId: 検証で用いる鍵を示す識別子
  • algorithm: 署名アルゴリズム。デフォルトは”hs2019” (RSASSA-PSS and SHA-512)、 "rsa-sha256" が指定できる
  • created: 署名の作成時刻
  • expires: 失効時刻
  • headers: 署名の対象となるメタデータ及びヘッダを指定する
  • signature: 署名値

署名の対象はheadersで指定された、正規化されたメタデータ及びヘッダ値を改行(\n)で連結したものです。
例えばこんな感じのものが署名アルゴリズムのインプットになります

(request-target): get /foo
(created): 1402170695
host: example.org
date: Tue, 07 Jun 2014 20:51:35 GMT
cache-control: max-age=60, must-revalidate
x-emptyheader:
x-example: Example header with some whitespace.

検証する側は同様に、HTTPメッセージの正規化を行い署名値を計算することによって検証を行います。