認証付き暗号の鍵利用上限について

認証付き暗号 "Authenticated Encryption with Associated Data (AEAD)" の利用上の制限について、「Usage Limits on AEAD Algorithms」というドキュメントが公開されている。

このドキュメントでは機密性と完全性を一定以上に保つために、一つの鍵の利用上限についてまとめています。

RFC 8446 TLS1.3」を読んだことがあれば、その仕様の中で「5.5. Limits on Key Usage」の章で、一つの鍵でAES-GCMでレコードを暗号化していい上限値を規定しているのをご存知かもしれません。また、同様の記述がQUICの仕様(URL)にも記述されています。(上限に達する前に鍵更新が行われます)


このように暗号の解析についてはいくつかの論文に分散しているため、プロトコルの標準化を行うために各仕様から参照できるように一つのドキュメントとしてまとめようということになっています。

ドキュメントは鍵について、2者間での利用ケースについて説明したSingle-User AEAD Limitsと、複数ユーザでの利用ケースについてMulti-User AEAD Limitsを説明しています。

ここでは、Single-User AEAD Limitsの上限について紹介します

前提

以下の変数を導入します

  • n: ブロックごとのbit数
  • k: AEAD鍵のbit数
  • t: 認証タグのbit数
  • l: 各メッセージのブロック数
  • s: 全てのメッセージの平文のブロック数
  • q: ユーザの暗号化の試行回数
  • v: 攻撃者の偽造試行回数
  • p: 攻撃者の攻撃確率

また、機密性及び完全性の特性を破る確率の増加率を優位性として定義します。

  • Confidentiality advantage (CA)
  • Integrity advantage (IA)

TLS1.3ではCAを2^(-60)、IAを2^(-57)になるように制限しています。

AEAD_AES_128_GCM、AEAD_AES_256_GCM

AEAD_AES_128_GCM、AEAD_AES_256_GCMにおける上限について紹介する

CAを下記に抑えるために

   CA <= ((s + q + 1)^2) / 2^129

ある仮定のもと、暗号化の上限回数は下記となる

   q <= (p^(1/2) * 2^(129/2) - 1) / (l + 1)

IAを下記に抑えるために

   IA <= 2 * (v * (l + 1)) / 2^128

下記の上限回数となる

   v <= (p * 2^127) / (l + 1)

その他

紹介しないが、同様に次の暗号アルゴリズムの上限回数がドキュメントでは紹介されている

  • AEAD_CHACHA20_POLY1305
  • AEAD_AES_128_CCM
  • AEAD_AES_128_CCM_8

ブラウザからTCP, UDPソケットを操作するDirect Sockets API

2020/01/14: 実際に動くのを確認しました
asnokaze.hatenablog.com

(2020/09/17 注釈: Raw SocketsからDirect Socketsに名称が変更されました)


ブラウザでTCP, DUPソケットを操作可能にする「Direct Sockets API」という仕様がW3CのWICGで議論されている。

f:id:ASnoKaze:20200830232335p:plain

また、blink-devでも「Intent to Prototype: Raw Sockets API」とプロトタイプの議論が行われている。

多くの方がセキュリティ上の懸念を抱くと思うが、ドキュメントでも慎重に検討すると書かれている。GithubでIssueを立てることも可能なので、思うことがある方は、まだまだ議論は始まったばかりでもあるので是非フィードバックされると良いと思う。(割と普通に聞いてもらえます)

なお、Raw Socketsという名称については変更も提案されているため、今後変更される可能性がある。

ユースケース

既存のプロトコルを話せるWebアプリケーションの作成が大きなユースケースである。例えば、SSHや, RDP, IRCなどが挙げられている。

API

今後、多くの変更

TCP

下記のように、リモートアドレスとポートを指定してTCP接続を開始する

const options = {
    remoteAddress: 'example.com',
    remotePort: 7,
    keepAlive: false,
    noDelay: false
};
navigator.openTCPSocket(options).then(tcpSocket => { ... }).else(error => { ... });

その後、readableStream, writableStreamで送受信を行う

UDP

UDPの場合は下記の通り

const options = {
    remoteAddress: 'example.com',
    remotePort: 7
};

try {
  const udpSocket = await navigator.openUDPSocket(options);
  doStuffWith(udpSocket);
  ...
} catch (err) {
  // handle error
} finally {
  udpSocket.close();
}

blobをsendする形でデー送信を行う。

let blob = ...;
await udpSocket.send(blob);

セキュリティ関連

まだまだ、多くのことを議論しなければならないし、対策方法についても議論の余地があると思うが、現在議論されている驚異は以下のとおりです。

  • MITM
  • ユーザの合意のない通信の開始
  • DDoS
  • CORSポリシーの回避
  • iframeによる利用
  • プライベートアドレス(内部ネットワーク)への接続

各脅威に対する対策方針の詳細はドキュメントを参照のこと

最後に

まだまだこれからだし、セキュリティ的には色々考慮事項が多いので注視していきたい

HTTP Client Hint Reliabilityの提案仕様

GoogleのDavid Benjamin氏から「Client Hint Reliability」という提案仕様が出されている。なおW3C WICG側でも議論行われている (URL)

まず、Client Hintについて説明してから、Client Hint Reliabilityの説明を行う。

HTTP Client Hint

HTTP Client Hint」という仕組みが、広く使われるようになっています。

HTTP Client Hintは、Webページを表示するにあたりヒントをクライアントからサーバに送ります。例えば、 DPR 解像度 をCH-DPRリクエストヘッダに付加して送信したりできます。

その他に、最近で言えばChromeがUserAgentヘッダの情報量を少なくし、代わりにより詳細なユーザエージェント情報を要求するのにHTTP Client Hintが利用されています。
asnokaze.hatenablog.com

このHTTP Client Hintでは、Accept-CHレスポンスヘッダでサーバがほしいと思うClient Hintのみを要求することで、不用意なクライアント情報の送信を抑制することが出来ます。

User Agent Client Hints の流れを簡単に以下に示す
f:id:ASnoKaze:20200722014902p:plain

  • クライアントはサーバにリクエストを送る
  • サーバは追加のUA情報としてUA-Archを要求する
  • クライアントは UA-Arch をサーバに送信する

Client Hint Reliability

上記で説明したように、Client Hintの仕組みでは、最初のリクエストにはサーバが必要とする情報がついてない場合があります。この問題に対応するのが「Client Hint Reliability」です。

Client Hint Reliability」では、下記の独立した2つのアプローチを提案している。

  • Critical-CH: 必要な情報をつけて送り直してもらう
  • ACCEPT_CH: 事前に、必要な情報を通知する
Critical-CH

新しくCritical-CHレスポンスヘッダを定義します。

Critical-CHヘッダは、必要なClient Hintsが無いのでリトライするようにクライアントに指示することができます。

例えば、下記のように使用する

     HTTP/1.1 200 OK
     Content-Type: text/html
     Accept-CH: Sec-CH-Example, Sec-CH-Example-2
     Vary: Sec-CH-Example
     Critical-CH: Sec-CH-Example

サーバは受け取ったリクエストヘッダにsec-ch-exampleがなかった場合、Critical-CH: Sec-CH-Exampleを含むレスポンスを返して、クライアントにこのClient Hintsを付加してリクエストをリトライするよう伝えることが出来ます。

ACCEPT_CH拡張フレーム

Critical-CHでは1RTT分のやり取りが発生してしまいました。それを回避する方法が、HTTP/2拡張フレームとして新しく定義するACCEPT_CHフレームを使う方法です。

フレーム定義は下記のとおりです。
f:id:ASnoKaze:20200722020506p:plain

内容は下記のとおりです

  • 適応するオリジン (HTTP/2では複数ドメインのリクエスト/レスポンスが多重化されるため)
  • Accept-CHの値

これは、Accept-CHヘッダ相当の情報を拡張フレームに詰めて送信できるということです。拡張フレームにすることで何が嬉しいかというと、クライアントからのリクエストを待たずしてAccept-CHヘッダ相当の情報を送信可能になる点です。

TLS1.3ではハンドシェイク後にサーバの方が先にHTTP/2のフレームを送信できます。また、TLS ALPSを利用することで、TLSハンドシェイク中にAccept-CH情報をクライアントに渡す方法も検討されています。

TLS ALPSを使うことで、最初のHTTPリクエストでサーバが必要なClient Hintsを付けることが出来るようになります。
f:id:ASnoKaze:20200722110301p:plain

TLS ALPSの紹介は下記を参照
asnokaze.hatenablog.com

HTTPSの接続情報を通知する "HTTPS DNSレコード" の提案仕様 (2021/07更新)

HTTPSで通信を開始する際に、事前に知っていると有用な情報が幾つかあります。

  • サーバがHTTP/3に対応しているか(Alt-Svc情報)
  • TLS Encrypted Client Hello で必要な情報
  • サーバがHSTSを要求してくるか

このような情報を提供する新しいDNSレコードを定義する仕様が提案されています。提案仕様「Service binding and parameter specification via the DNS (DNS SVCB and HTTPS RRs)」では、サービスに関する情報を通知する一般形式であるSVCBレコードと、HTTPS用であるHTTPSレコードを定義しています。

もともとはHTTPSSVCレコードという名称でしたが、最近改称されHTTPSレコードになりました。

また、このHTTPSレコードを用いることでANAMEレコードが解決しようとしていた、APEXを別のレコードに飛ばすことも可能です。

簡単に紹介していきます。

実装状況

Chromeがすでに実装を開始しています

また、CloudFlareではすでに、このHTTPSレコードを返すようになっています (レコードタイプ 65)。下記はうまくデコード出来てませんが、HTTP3のドラフト版対応を通知してます。

$ dig  blog.cloudflare.com type65 +short
;; ANSWER SECTION:
blog.cloudflare.com.    294     IN      TYPE65  \# 76 000100000100150568332D32390568332D32380568332D3237026832 0004000868121A2E68121B2E00060020260647000000000000000000 68121A2E26064700000000000000000068121B2E

概略

HTTPSは下記のような形式をしています。

Name TTL IN HTTPS SvcPriority TargetName SvcParams

具体例を見ていきましょう

   pool  7200 IN HTTPS 1 h3pool alpn=h2,h3 port="443" echconfig="123..."
                 HTTPS 2 .      alpn=h2 echconfig="abc..."

   pool   300 IN A        192.0.2.2
                 AAAA     2001:db8::2
   h3pool 300 IN A        192.0.2.3
                 AAAA     2001:db8::3

HTTPSレコードの中身の最初の値は優先度で、異なる優先度でHTTP/3用とHTTP/2用のレコードがあります。

1つ目のHTTPSレコードは、TargetNameとしてh3poolという指定してきます。このe3poolのエンドポイントが、poolサブドメインalternative endpointとして動作させる事ができます (RFC7838 HTTP Alternative Services 参照)

SvcParamsとして、使用するポート番号、HTTP/2やHTTP/3に対応していることを示すalpnの識別子、TLS Encrypted Client Helloの情報を付加しています。

また、HTTPSレコードを使用しているだけで、このドメインに対してHTTP Strict Transport Security(HSTS)が適応されます。

利用状況

2021年5月時点での利用状況です。Akamaiだとトラフィックがそれなりに流れているようです。
https://pbs.twimg.com/media/E0yuQPsVkAoY7n1?format=jpg&name=large

なお。Appleは、HTTP/3の接続時にこの情報を利用することを表明しています。

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レイヤで保存しておけるといった利点があります。

なるほど。