CloudFlareの提案するHTTP/2の圧縮辞書拡張

IETFのHTTPbis wgでCloudFlareの方より「Compression Dictionaries for HTTP/2」(URL)という仕様が提案されている。


これは、Content-Encodingヘッダで指定される圧縮アルゴリズム向けの初期ウィンドウに使用される辞書データを事前に送る拡張を定義する。


CloudFlareではすでに検証されており、通常のDeflate(zlib compression level 8))より最大1.50倍、平均で1.10倍縮小できたとしている。

背景

  • CSS/JS/HTMLといったアセットは、Content-Encodingを使用しDeflateやBrotliで圧縮される
  • HTTP/2以前は、リクエスト数を少なくするためにインライン化などを行っていた
  • 圧縮アルゴリズムは、後方一致するほど圧縮率が上がる
  • HTTP/2で小さいファイルを多重化して送ることが推奨化されており、圧縮率が上がらない。

拡張フレーム

この仕様では、2つの拡張フレームを定義している。大雑把に言うと辞書を宣言するフレームと、辞書を適応するフレームである。

SET_DICTIONARYフレーム
   +-------------+-------------+
   | Dict ID (8) |   Size (8)  |
   +-------------+-------------+
  • Dict ID: 辞書のためのスロットを指定する値
  • Size: 辞書のサイズ
USE_DICTIONARY
   +-------------+
   | Dict ID (8) |
   +-------------+

Dict ID: SET_DICTIONARYでセットされた辞書のスロットを指定する

動作

サーバ側

サーバは、最初のDATAフレームを送る前にいかなるストリーム上でSET_DICTIONARYフレームを送信できます。サーバは最初の2^Sizeの圧縮されてないオクテットを後続のストリームのためのCompression Dictionaryとして使用します。


サーバはストリームでDATAフレームを送信する前にUSE_DICTIONARYフレームを送ることで、Compression Dictionaryでストリームを圧縮できます。

クライアント側

クライアントはSET_DICTIONARYフレームを受信するとそのサイズ分だけ辞書のためにスロットを予約します。
そのストリームでDATAを受信したあと、最初の2^Sizeオクテットを辞書として保存します。


USE_DICTIONARYを受け取った際、クライアントはDATAの複合に指定された辞書を使用します。

googleの新しい時刻同期プロトコル Roughtimeとは

[追記] 2020年1月時点の動向について、新しく記事を書きました
asnokaze.hatenablog.com


Googleの「Adam Langley氏のブログ」で、新しい時刻同期プロトコルについて紹介されている。このRoughtimeは特定のタイムサーバに依存しないセキュアな方法で時刻同期を行うことを目的としたプロトコルです。すでに、googleのサーバで動作しており、roughtime.sandbox.google.com:2002に向けて公開されている専用クライアントで接続できる。


現在のセキュリティは現在の時刻に依存しており、その重要性はましています。証明書の有効期限や、OCSPレスポンス、Kerberosのチケット、DNSSECの応用やPGP鍵といった機能でも重要です。しかし、Chromeの証明書エラーのうち25%はローカルの時刻に起因するとしているとしています。


現在、最も使用されているNTPプロトコルの認証機能はほとんど使用されていないとし、MITM攻撃で時刻を操作することが出きる旨記述している。


この問題に対して、それらを改善することを目的に新しい時刻同期プロトコルroughtimeを提案している。

roughtimeの特徴

  • UDPプロトコル
    • 増幅攻撃を防ぐため、リクエストメッセージは1kバイトになるようにパディングされる
  • 現段階では正確な時刻同期を目標としておらず、10秒以下であれば満足とする
  • 認証されているタイムサーバでも複数のタイムサーバを用いて正しくない振る舞いをするタイムサーバを検出する
  • 公開鍵方式で署名される
    • Ed25519のみをサポートしてる
    • サーバのレスポンスは、クライアントが生成して提出したノンスと時刻について署名して応答される
    • long-term identity key から委譲されるonline public keyを使用する
    • 複数のリクエストに対してバッチ的に署名する (Merkle treeのルートに対して署名する)
    • レスポンスに含まれるMerkle treeは、実際には対象のノンスから、ルーに到達するのに必要なノードとindex(left or rigth)が与えられる(木のリーフは1~64)
    • Skylakeチップで1コアで1秒あたり430万リクエストを署名できると見積もっている
  • うるう秒を24時間に分割して時刻に反映する

roughtimeプロトコル概要

クライアントは乱数を生成し、それをnonceとしてRoughtime リクエストをサーバに送信します(1024バイトまでパディングされる)。サーバはそのノンスと時刻に対する署名、サーバ証明書などをroughtimeレスポンスとして返します。


この時、クライアントが生成したノンスに対して署名されて返ってくるため、その時生成されたことが保証されます。


つまり、サーバAに問い合わせて結果を得たあとに,サーバBに問い合わせた場合。サーバAから得られた時刻 より、サーバBから得られた時刻のほうが新しいはずということがわかります。この時サーバBに問い合わせるときに、サーバAからの応答をハッシュしてノンスとして使えば、その順序も確実であるといえます。それぞれから得られた時刻で矛盾があればなにかおかしいことに気づくことができます。


この手続を6つ以上の独立したサーバで行えば不正なサーバを検出でき、正しい時刻を定めるのに十分正確だとしています。

roughtimeメッセージ

Roughtime メッセージ(リクエスト or レスポンス)は、タグの数を示すヘッダと、複数のタグと値のペアからなります。タグ名は32bit値であり、ドキュメント中では文字として"NONC "や"CERT"と記述されます。3文字の場合は4文字目だけ16進数で明示的に記述されます("SIG\x00")。タグはそれぞれ値をもちます。

roughtime リクエス

Roughtime リクエストは、NONCタグを持つメッセージです。NONC の値は64バイト値です。それ以外のタグは無視されます。増幅攻撃を防ぐためにPAD\xffタグを用いて1024バイト以上にする必要があります。

Roughtimeレスポンス

レスポンスは以下のタグで構成されます

  • SREP: 署名されるメッセージ。Merkle treeの根と、時刻を示す2つのタグMIDP,RADIを含みます
  • SIG\x00: SREPの署名値である64バイト値。Ed25519 のみが署名アルゴリズムとしてサポートされており。
  • CERT:サーバの証明書。署名に使用するonline public keyと有効期間と、この鍵に対する署名。
  • INDXPATH: 複数のクライアントのリクエストに含まれるノンスはMerkle treeの葉となる。複数のリクエストに対して署名を行うため、Merkle treeのルートのみに署名を行う。このMerkle treeをルートまでたどるのに必要な経路PATH(ノードのリスト)と、たどる際のノードが左右のどちらのノードなのかを示すINDX
Roughtime UTC

タイムスタンプはMIDPRADIの2つのタグで表現されます。MIDPタグはマイクロ秒で時刻範囲の中心点をunit64として持ちます。一方RADI タグは時刻範囲の半径をマイクロ秒でuint32で表現します。


Roughtime の"正しい時刻"は、24時間でうるう秒をならしたUTCです。1日かけて均等に反映されていきます。

実装

C++ と Goの実装が公開されている。 C++はbazelを導入し、ビルドする必要があるが、Goは比較的すぐに動作確認することができる。

vagrant@vagrant:~/go/gopath/src$ git clone https://roughtime.googlesource.com/roughtime roughtime.googlesource.com

vagrant@vagrant:~/go/gopath/src/roughtime.googlesource.com/go/client$ go build

vagrant@vagrant:~/go/gopath/src/roughtime.googlesource.com/go/client$ ./client --servers-file=../../roughtime-servers.json --chain-file=$HOME/roughtime-chain.json
Quorum set to 1 servers because not enough valid servers were found to meet the default (3)!
real-time delta: -16.306032ms


roughtime-servers.jsonGoogleのサーバとその公開鍵が指定されており時刻の誤差が表示される、一つしかないのでサーバの検証は実施されない。


20/10/11 表現を少々修正しました
前) 正確な時刻同期を目標としておらず、10秒程度の誤差は許容する
後) 現段階では正確な時刻同期を目標としておらず、10秒以下であれば満足とする

XSSを防ぐ新しいXSS-Protectionヘッダ

evalとreportOnlyについて追記しました (2016/10/10)


2016/10/20
仕様名は以下の通りになりました。
Anti-XSS Response-Time Uniqueness Requirement


また、ヘッダ名は、XSS-Protectionヘッダではなく、ARTURヘッダとなっておりますが、また変更される可能性があります。


Googleの調査によると、CSPによるXSSの防止は現実的にデプロイの欠陥によりXSSの防止効果がないことを示しています。調査は「CSP Is Dead, Long Live CSP!」としてACMのカンファレンスで発表され、ペーパーも閲覧することができます。


9月に行われたW3C TPAC 2016のWebAppSecのミーティングで議論され、GoogleのMike West氏より新しくXSS Protectionという仕様が提案されている。仕様は氏のリポジトリから確認することができる。
https://mikewest.github.io/artur-yes/


このXSS Protectionは、新しくXSS-Protectionヘッダを定義し、CSPの一部の機能を引き取りX-XSS-Protectionでサポートされているような機能を有します。目的は簡潔でわかりやすく、CSPと同等のXSS軽減機能を提供することです

XSS-Protectionヘッダ

XSS-Protectionヘッダは、json形式のヘッダ値を持ちます。
これについては、現在IETFで議論されている「A JSON Encoding for HTTP Header Field Values」という仕様を使用しています。

下記の例では、XSS-Protectionヘッダでノンス値を指定します。その場合、そのノンス値が指定されたスクリプトタグのみが実行されます。

XSS-Protection: { "nonce": "abcdefg" }
<!-- このスクリプトは実行される -->
<script src="script.js" nonce="abcdefg"></script>

<!-- このスクリプトは実行されません -->
<script src="script.js"></script>

指定できる項目

eval

XSS-Protectionでは、デフォルトではevalはブロックされますがevalメンバーにunsafe-allowを指定することで、hashなどの条件が一致する場合は実行できるようになります

XSS-Protection: { "hash": [ "sha256-abcd...", "sha256-zyx...", "eval": "unsafe-allow" ] }
hash

実行を許可するスクリプトハッシュ値を指定する

XSS-Protection: { "hash": [ "sha256-abcd...", "sha256-zyx..." ] }
<script src="script.js" integrity="sha256-abcd..."></script>
<script>
  // Content which hashes to abcd... goes here.
</script>
nonce

実行を許可するScriptに指定されるノンス属性の値を指定

XSS-Protection: { "nonce": "abcdefg" }
<script src="script.js" nonce="abcdefg"></script>
reflection

反射型XSSに対するポリシーに対して、ignore, filter, blockを指定する。ignoreの場合はユーザエージェントの保護機能を無効にする、filterはユーザエージェントのXSS検知機能で、検知された場合はそのスクリプトの実行をブロックする。blockは、ユーザエージェントのXSS検知機能で検出された場合にそのページのロード自体をブロックする。

XSS-Protection: { "reflection": "block" }
report

Reporting APIに則ってレポートグループを指定する。
reportOnlyを指定することで、ブロックはせずレポートだけを送ることもできます。

XSS-Protection: { "nonce": "abcdefg", "report": "group1" }
Report-To: { "url": "https://example.com/report", "group": "group1", "max-age": 10886400 }

Chrome 56 のHTTPサイトへの日本語版警告

Moving towards a more secure web」でアナウンスされているように、2017年1月にリリースされる予定のChrome 56でHTTPサイトへの警告が表示されるようになる。


日本語のサイトでも取り上げられている

日本語版表示

Chrome Canaryで、chrome://flagsより 下記設定を有効にすることで、日本語版の警告表示が確認できる。

  • "保護されていない発行元に「保護されていない発行元」のマークを付ける"


http://asnokaze.com


https://asnokaze.com


日本語だと、また印象が変わる。HTTPS対応できてないサイトへの影響は結構ありそうである。

Secure Contextsに関する localhost と、IETFでの新提案

Secure Contexts

Service Workers、Web BluetoothといったAPIは、安全に使用するためにセキュリティ上の条件があります。


その条件がSecure Contextと呼ばれるコンテキストであり、W3CのSecure Contexts(URL)というドキュメントで定義されています。


このSecure Contextsの仕様上で localhost. の扱いがどうなっているかというと次のようになっている。RFC6761の定義では「ローカルのリゾルバはlocalhost. 及び .localhost. の内側のドメインを特殊扱いしてもよい、すべき(MAY/SHOULD)」と書かれており、その不確実性のためlocalhostではなく127.0.0.1の場合に特別扱いするとしている。

つまり、localhostドメインをローカルネットワークのDNSに問い合わせても良いため、ループバックアドレスという保証が無いことになる。

新提案

そういった状況に切り込んだのが、W3CのWebAppSecでは大変良く見かけるMike West氏である。


IETFに「Let 'localhost' be localhost.」(URL)という、I-Dを提出している。


この提案では、localhost. はループバックアドレスになるように、localhostの名前解決でSHOULDとなっていた部分をMUSTへ変更する提案です。


必ずlocalhost.を特別扱いし(MUST)、DNSにクエリを送信することも許可されません(MUST NOT)。あわせて、DNSサーバも問い合わせに対してNSレコードを検索しようとしてはいけません(MUST NOT)。既存のlocalhost.の扱いを幾つか変更することで、必ずループバックアドレス扱いになるようにし、Secure Contextsでの問題を解消しようとしています。


提案仕様の最後に、Implementation Considerationsとして、サーバの名前にserver1.localhostといった名前をつけている場合は予期せぬ挙動になるという懸念を挙げています。


引き続き、Secure Contextsとlocalhostの議論は続くと思いますが、個人的には興味深いなと思っています。

wicgのFace detection API

wicgで議論になっている「Face detection API」の仕様が「Shape Detection in Images」としてwicgのリポジトリで公開されている。


https://wicg.github.io/shape-detection-api/

Shape Detection in Images

HTMLImageElement、HTMLCanvasElement、ImageDataといった画像のデータを与えることで、顔領域の矩形が得られるようである。


現状は、顔を検出する「Face Detection API」と、バーコードを検出する「Barcode Detection API」が定義されている。


具体的にはexampleを見るとわかりやすい

let faceDetector = new FaceDetector({fastMode: true, maxDetectedFaces: 1});
// Assuming |theImage| is e.g. a <img> content, or a Blob.

faceDetector.detect(theImage)
.then(detectedFaces => {
  for (const face of detectedFaces) {
    console.log(' Face @ (${face.boundingBox.x}, ${face.boundingBox.y}),' +
        ' size ${face.boundingBox.width}x${face.boundingBox.height}');
  }
}).catch(() => {
  console.error("Face Detection failed, boo.");
})

HTTP/2のデバック用情報エンドポイントの仕様

HTTP/2 Implementation Debug State

「HTTP/2 Implementation Debug State」というHTTP/2用のデバッグ情報を表示するエンドポイントの仕様が、IETFに提出されています。

https://tools.ietf.org/html/draft-benfield-http2-debug-state-01


サーバの「.well-known/h2/state」にアクセスすることでそのコネクションの状態を表示するサーバ側のエンドポイント、及びその内容を定義しています。

mod-h2 http2-status

mod-h2のversion 1.6.0より http2-status が、この仕様に準拠したので簡単に動作確認する。


ubuntu16.04(openssl 1.0.2)で、今回は svn のtrunkからビルドする

#nghttp2をインストールしておく
sudo apt-get install -y  libtool libtool-bin libpcre3-dev autoconf libssl-dev libxml2-dev libev-dev build-essential 

svn checkout http://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x httpd-2.4.x
cd ../httpd-2.4.x/
svn co http://svn.apache.org/repos/asf/apr/apr/trunk srclib/apr
./buildconf

./configure
make

証明書の設定及びSSL(TLS)とHTTP/2を有効にし、confにhttp-2statusの設定を加える

   Protocols h2c http/1.1
   <Location "/.well-known/h2/state">
        SetHandler http2-status
   </Location>

結果

/.well-known/h2/state

{
  "version": "draft-01",
  "settings": {
    "SETTINGS_MAX_CONCURRENT_STREAMS": 100,
    "SETTINGS_MAX_FRAME_SIZE": 16384,
    "SETTINGS_INITIAL_WINDOW_SIZE": 65535,
    "SETTINGS_ENABLE_PUSH": 1
  },
  "peerSettings": {
    "SETTINGS_MAX_CONCURRENT_STREAMS": 1000,
    "SETTINGS_MAX_FRAME_SIZE": 16384,
    "SETTINGS_INITIAL_WINDOW_SIZE": 6291456,
    "SETTINGS_ENABLE_PUSH": 1,
    "SETTINGS_HEADER_TABLE_SIZE": 4096,
    "SETTINGS_MAX_HEADER_LIST_SIZE": -1
  },
  "connFlowIn": 2147483647,
  "connFlowOut": 15707901,
  "sentGoAway": 0,
  "streams": {
    "41": {
    "state": "HALF_CLOSED_REMOTE",
    "created": 1473179424.329667,
    "flowIn": 65535,
    "flowOut": 6291456,
    "dataIn": 0,
    "dataOut": 0
    }
  },
  "stats": {
    "in": {
      "requests": 21,
      "resets": 0, 
      "frames": 24,
      "octets": 1250
    },
    "out": {
      "responses": 20,
      "frames": 43,
      "octets": 21712
    },
    "push": {
      "cacheDigest": "AQg",
      "promises": 0,
      "submits": 0,
      "resets": 0
    }
  }
}


幾つかの仕様上でオプショナルな物は表示されない。例えばHPACKのダイナミックテーブルの情報なども仕様上は定義されているが、セキュリティ上の理由により検討して仕様すべきとされている。