(RFC8586) Forwarding-Loop Attacks攻撃を防ぐCDN-Loopヘッダの提案仕様

RFC 8586 Loop Detection in Content Delivery Networks (CDNs)として、標準化されました (2019/4/25 追記)


Forwarding-Loop Attacks攻撃を防ぐ「CDN Loop Prevention」 という仕様が提案されています。

背景

CDNへのDoS攻撃として、リクエストをCDN内でループさせる「Forwarding-Loop Attacks」という攻撃手法が知られています。

CDNユーザの設定ミスや、攻撃者がユーザとしてそのような設定をすることでHTTPリクエストをCDN内でループさせる攻撃です。詳しくは「Forwarding-Loop Attacks in Content Delivery Networks」(PDF) にかかれています。

単一のCDNや、もしくは複数のCDNをループさせる方式があります。

このForwarding-Loop Attacksを防ぐために、ループを検知するための「CDN-Loopヘッダ」を定義する提案仕様がAkamai TechnologiesとFastlyとCloudflareの方の共著で出されています。

CDN-Loopヘッダ

CDN Loop Prevention」では、ループ検知のためにCDN-Loopヘッダを新しく定義します。このCDN-Loopヘッダに経由したCDNを記録することで、CDNのループを防ぎます。

本来であれば、ループ検知はRFC7230で定義される「via」ヘッダを使用すべきですが、viaヘッダをその他の目的のために使用するサーバもいるため実際にはviaヘッダ使用できません。

CDN-Loopは下記のとおりである。コンマ区切りでCDNの識別子が中継されるたびに追記されていきます。もしくは、新しくCDN-Loopを増やしても構いません。また、セミコロンの後ろに任意のkey/value値を付けることも出来ます。(これは、Structured Headers for HTTPのParameterised Listsに順している模様)

CDN-Loop: FooCDN, barcdn; host="foo123.bar.cdn"
CDN-Loop: baz-cdn; abc="123"; def="456", anotherCDN

CDN及びその他の中間装置は、このCDN-Loopを削除してはいけません(MUST NOT)。そのため、オリジンサーバにもこのCDN-Loopが届くことになります。

多くのCDNでは設定でヘッダを改変することも出来ます。そのことに言及はあるものの、いまのところ具体的な署名方法などについては述べられていません。今後議論されることになると思います。

Chromeが6週間毎にTLSバージョン番号を変更していくかもしれない

[修正] ossificationを骨化と訳してましたが、硬直化に変更しました


TLS1.3の標準化が終わり、もうRFCとして出されるのを待つばかりになっている。

そんなTLSワーキンググループのメーリングリストに、ChromiumやBoringSSLの開発に携わるDavid Benjamin氏より「Enforcing Protocol Invariants」というメールが投稿されています。

これは、今後もTLSの改善可能とするために、Chromeで新しいTLSバージョンコードポイントを試していくことに対する意見を募集しているものです。

実装としてはTLS1.3と同じですが、使用するバージョン コードポイントが異なるものを使用していくことになります。

背景、ossification(硬直化)問題

この議論の背景にはTLS1.3の標準化に関する、ossification(骨化)と呼ばれる問題に対する非常につらい道のりがあります。

TLS1.3の標準化作業を進めてるとき、ChromeFacebookが実験的にTLS1.3を試していくと、世の中のファイアウォールやネットワーク上の中間機器がTLS1.3を正しく解釈できず不具合を起こすことがわかりました。

そのため、TLS1.3のバージョンネゴシエーションはそれ専用のsupported_versions拡張を定義して、そこでバージョンネゴシエーションを行います。しかし、そのsupported_versions拡張の拡張番号を勝手に使用している実装が存在したり、TLS1.2のメッセージ順序を想定した実装などにより通信がブロックされたり、フィールドテストを繰り返し問題が見つかるたびTLS1.3の仕様を修正してきました。

そのような変更の道のりは下記資料が詳しいです

このようなインターネット上に存在する正しくない実装によって、新しいプロトコルのデプロイが阻害される状態の事をIETFではossification(硬直化)と呼んでいます。

6週間ごとのTLSバージョンコードポイント試していく

将来もTLSの改善を進めていけるように、上記のような正しくない実装の存在を明らかにしossification(硬直化)を防ぐために、新しいTLSバージョンコードポイント(拡張やcipher suites)を試していくというのが、今回の議論です。

錆びついたTLSを滑らかに、GoogleによるGREASE試験 - ぼちぼち日記 のような議論もありますが、さらにアグレッシブにバージョン番号さえもGREASEを進めていく形です。

この取組では、Chrome及びGoogleのサーバで2つのTLS1.3バージョンをサポートしていくといってます。

  • TLS1.3標準のバージョン番号 (0x0304)
  • rolling alternate version

rolling alternate versionが6週間ごとに変更されるバージョンということで、TLSの正式バージョンである0x03... をさけて採番していく考えがあると言ってます。

まだまだ、案だしの段階であり具体的にどうこうという状況ではなさそうです。

コメント

すでにコメントが付いていて、反対意見は今の所ないようです。Appleの人も興味深いと好意的な意見を述べています。

googleだけでなく他の実装者もこのバージョンローリングに対応していくかもしれません。

6週間ごとにローリングしていく番号をドキュメント化するのは大変だが、wikiなどで記述できると良いよねというコメントもついている。

Network Error Loggingを用いたポートスキャン手法について

20180711追記 この問題は解決されました
Handle errors in different phases differently #83

  • include_subdomains はDNSの名前解決エラーのみをレポート出来、TCP関連のエラーは通知できません。
  • ReportingObserver で、Network Error Loggingは監視できません
  • DNS Rebinding を防ぐため、ポリシーを適応したIPアドレスを覚えておきます。IPアドレスが変更した場合レポートは送信されません

先日、紹介した「Network Error Logging」を使用することでユーザのプライベートネットワークをポートスキャン可能であることを開発者様に報告した。

その手法と、仕様上の問題点について説明する。

仕組みは単純なので、もっと早く気づけても良かったかもしれない...orz

Network Error Logging

前回紹介したとおり、ブラウザで発生するネットワークエラーをレポートとして受け取れるようになる仕様である。
asnokaze.hatenablog.com

レスポンスを返す際に、nelヘッダを指定すると、そのページを閲覧している際に発生する、TCPコネクションタイムアウトTLS証明書エラー・名前解決エラー・リダイレクトループなどが、レポートとして指定したエンドポイントに提出される。

nel: {"report-to": "network-errors", "max-age": 360, "include-subdomains": true,}

例えば、画像が404で読み込めなかったりすると、こんな感じのJSONが指定したエンドポイントにPOSTされる

	{
		"age": 240003,
		"report": {
			"elapsed-time": 33,
			"protocol": "http/1.1",
			"referrer": "https://asnokaze.com/nel.html",
			"sampling-fraction": 1,
			"server-ip": "160.16.124.39",
			"status-code": 404,
			"type": "http.error",
			"uri": "https://asnokaze.com/img/b.gif"
		},
		"type": "network-error",
		"url": "https://asnokaze.com/img/b.gif"
	},

ポートスキャン手法

Network Error Loggingでは、include-subdomainsを指定することでサブドメインのリソースに対するネットワークエラーもレポート対象に含めることができる。

例えば、example.comサブドメインとしてプライベートIPのサブドメインを用意する。具体的には127.0.0.1と名前解決されるような127-0-0-1.example.comという感じである。

example.comへのアクセスで、include-subdomainsを指定してnelヘッダを返してやる

NEL: {"report-to": "nel", "include-subdomains": true,
      "success-fraction": 1.0, "failure-fraction": 1.0}

あとは、HTML内に、こんな感じで用意したサブドメインへリクエストが飛ぶようにしてやる。

<img src="https://127-0-0-1.example.com:2000" />
<img src="https://127-0-0-1.example.com:2001" />
<img src="https://127-0-0-1.example.com:2002" />
<img src="https://127-0-0-1.example.com:2003" />

Network Error Loggingでは、以下のようなTCPエラーが定義されているため、既存の手法(onerrorイベント)よりも多くの情報を得ることが可能である

  • tcp.timed_out TCP接続タイムアウト
  • tcp.closed TCP接続がクローズされた
  • tcp.reset TCP接続がリセットされた
  • tcp.refused TCP接続が拒否された
  • tcp.aborted TCP接続が中断された
  • tcp.address_invalid TCP接続のアドレスが正しくない
  • tcp.address_unreachable TCP接続のアドレスへ到達できない

直接的になにかの脅威があるわけではないが、何かしらの攻撃の足がかりになる可能性も考えられる

仕様上の問題点

すでに上記問題は、仕様の問題として議論がされている
https://github.com/WICG/network-error-logging/issues/74

主には2点

  • サブドメインのリソースをレポート対象に含めるべきか。CORSのようなそのサブドメインで動作するサーバの許可を得るか、サブドメインの指定をそもそもできなくする
  • TCPのエラー情報などを実際に取得可能にしてよいのか。例えば、Fetch APIでは詳細エラーを取れるようにしてほしいという意見が出るが、今の所そのようにはなっていない。

Same-Origin Policyを超えて設定が指定されること、サブドメインにプライベートIPが指定されうることは、結構良くないパターンがありそうですね。パットは思いつかなかったが、他の仕様でもなんかあるかもしれない。(cookieとか)

あらためて、新しい機能を作るって難しいなと実感しました。

Cross-Origin-Resource-Policyヘッダとは

Cross-Origin-Resource-PolicyヘッダというのがSafari 12でサポートされるらしい

もともとは、W3C側でFrom-Originと呼ばれていた仕組みらしいが、Fetch Starndardに入れる議論がされているようだ。

github.com

このCross-Origin-Resource-Policyヘッダを用いることで、自分サイトでホストしている画像やJavaScriptなどのリソースをクロスオリジンで他所のサイトで表示・利用されることを防ぐことができる。

https://example.com/img.jpg を<img>タグで呼び出せるのはexample.comのサイトだけで、https://asnokaze.comから<img>タグで画像を埋め込んでも表示することができなくなる。

Cross-Origin Read Blocking (CORB) とは」でも書いたように、昨今サイドチャネルなどの攻撃によってリソースなどのデータが第三者に読み取られる可能性があるため、それらの攻撃を防ぐのを目的にしている。( Issue参照 )

サイドチャネルを行う目的のサイトで、本人しか見えない画像や、http://localhost/img.pngを読み込まれて、中身を見られたりしたら困るわけだ。

Cross-Origin-Resource-Policyヘッダ

画像やJavaScriptなどのリソースを返すときに、レスポンスヘッダにつける。

Cross-Origin-Resource-Policy: same-origin

(WWDCではヘッダ値はsameと書かれていたが、Fetchの仕様ではsame-origin/same-site)

  • same-originは同一オリジンの場合のみ許可される
  • same-siteはhttpsかつ同じホスト名の場合のみ許可される

まだ議論中なので、実際どうなるかはわからない

QUICの現状確認をしたい 2018/5 (Stream 0の再設計案などなど)

IETFでは引き続き、QUICの標準化が進められています。


次回
asnokaze.hatenablog.com

前回分
asnokaze.hatenablog.com


現状確認したい

ちょうど先週に、コアドキュメントのdraft-12も出ています

例えば大きなところで、draft-11で「Coalescing Packets」が入り複数のQUICパケットを一つのUDPデータグラムに格納できるようになった。draft-12ではパケット番号の暗号化などの更新が含まれています。これによって、通信経路上ではパケットを観察してもほとんどの情報が見えなくなりました。

また、HTTPヘッダの圧縮の仕様であるQPACKもwg draft-00が出し直されており、大きな差分があります。

ホットトピックとして、後述するStream 0の再設計や、ECNサポートがあります。
6月4~5日に行われる、QUIC WGの中間会合で議論されることでしょう。
wg-materials/agenda.md at master · quicwg/wg-materials · GitHub

相互接続テスト状況

QUICでは、draftバージョンの実装を持ち寄って相互接続テストを定期的に行っています。
現在は、「5th Implementation Draft」としてテストしています。

f:id:ASnoKaze:20180530013031p:plain

Stream 0 Design Team

draft-12のQUICでは、Stream 0上でTLS1.3のメッセージをやり取りして、そこから通信を暗号化するための鍵を共有します。

このStream 0上でのTLS1.3のやりとりする設計は、IETF101で問題が提起されました。その時の発表資料は、「wg-materials/Stream0-EKR.pdf at master · quicwg/wg-materials · GitHub」で公開されています。

以前以下の記事でも書いたとおり、Stream 0上でCrypto handshakeを行うため、Stream 0の再送や信頼性について特殊扱いすることになっています。ストリームは転送順番やロスが回復されますが、そのためのメッセージもQUICでは暗号化されますが、Stream 0では鍵の共有を行っている途中というわけです。(僕の認識が違ったらすみません)

QUIC over DTLSの議論も行われましたが、変更が大きすぎる点やDTLS自体への変更も考える必要があるため、QUIC over DTLSの方向ではなく、Stream 0を再設計する方針となりました。
asnokaze.hatenablog.com

Stream 0 の再設計には、デザインチームが結成されました。
この「Stream 0 Design Team」は、MLにかかれている通り、ガチ勢ばかりです。

Stream0 Design Team Proposal

そのStream 0 Design Teamによる、提案が出てきました。

Documentの方の図を見ると、どのような変更がされたのかがよくわかります。

draft-12では以下のような感じです
f:id:ASnoKaze:20180530011328p:plain
stream 0でTLSレコードをそのまま送信します。TLSレコードには通常通り、ServerHello, EncryptedExtensions, Fin, NewSessionTicketsなどのTLSメッセージが含まれています。


提案方式では
f:id:ASnoKaze:20180530012004p:plain
Stream 0を撤廃し、新しく定義されたCRYPTO_HSフレーム上にTLSレコードではなく、TLSメッセージを直に設置しています。Stream 0を撤廃することで、Stream 0の特殊扱いはなくなります。
(CRYPTO_HSフレームは、CRYPTOフレームに改名されるかも)


おわり

この変更により、パケット番号空間や再送らへんもいろいろ変更が入っています。ちょっと細かいところまで理解が進んでいないので、時間をかけて差分を読んでいきたいです

ブラウザのネットワークエラーをレポートさせるNetwork Error Loggingが来た

20180727追記
CORS対応が必要になりました
asnokaze.hatenablog.com

20180703追記
ドキュメントはhttps://w3c.github.io/network-error-logging/ にが移されました

20180608追記
仕様上は、jsonの各値はハイフンではなく、アンダースコアを使用するようになります

  • report-to => report_to
  • max-age => max_age
  • ... etc

https://github.com/WICG/network-error-logging/commit/86c4d1c0fa4c5d5ca1d8bdcd9fa931e7e4ab65c2

こんな感じ

nel: {"report_to": "network-errors", "max_age": 2592000, "include_subdomains": true, "success_fraction": 1}
report-to: { "endpoints": [{"url" : "https://asnokaze.com/report", "priority": 1}], "group": "network-errors", "max_age": 10886400, "include-subdomains":true}


以下本編


TCPコネクションタイムアウトTLS証明書エラー・名前解決エラー・リダイレクトループなど、ブラウザでは様々なネットワークエラーが発生します。ユーザ側の環境にも依存するため、サーバ側からは検知できない場合もあります。

そういったブラウザで発生したネットワークエラーをレポートさせる「Network Error Logging」がついに、Chrome開発版(dev) で利用できるようになったので試してみる

たまにdisableになってたりするけど、再起動すると使えたりする。。。

Network Error Logging

Network Error Logging」は、W3CのWICGで議論されている仕様です。

HTTPレスポンスヘッダで、nelヘッダを指定することでネットワークエラーを指定したエンドポイントに対してレポートするようになります。レポート先エンドポイントは「Reporting API」で定義されるように、report-toヘッダで別途指定します。

指定例

nel: {"report-to": "network-errors", "max-age": 3600, "include-subdomains": true,"success-fraction": 0}
report-to: { "endpoints": [{"url" : "https://asnokaze.com/report"}], "group": "network-errors","max-age": 10886400, "include-subdomains":true}

nelヘッダでは以下のような指定ができます

  • report-to: エンドポイントのグループ識別子(別途report-toヘッダで指定する)
  • max-age: このヘッダで指定される指示の有効期限
  • error-fraction: エラー発生時にレポートを送信する割合。1.0~0.0の間で指定
  • success-fraction: エラーが発生していなくてもレポートを送信する割合。1.0~0.0の間で指定
  • include-subdomains: サブドメインのネットワークエラーもレポートするか

このように指定すると、以後そのドメインでネットワークエラーが発生した場合に以下のようにレポートがPOSTされる。
404エラーが発生した場合と、名前解決エラーが発生した場合のレポート例です(jsonは整形済み)

どのページで、どういたネットワークエラーが発生したか確認できる。

[
	{
		"age": 240003,
		"report": {
			"elapsed-time": 33,
			"protocol": "http/1.1",
			"referrer": "https://asnokaze.com/nel.html",
			"sampling-fraction": 1,
			"server-ip": "160.16.124.39",
			"status-code": 404,
			"type": "http.error",
			"uri": "https://asnokaze.com/img/b.gif"
		},
		"type": "network-error",
		"url": "https://asnokaze.com/img/b.gif"
	},
	{
		"age": 147358,
		"report": {
			"elapsed-time": 7,
			"protocol": "",
			"referrer": "https://asnokaze.com/nel.html",
			"sampling-fraction": 1,
			"server-ip": "",
			"status-code": 0,
			"type": "dns.name_not_resolved",
			"uri": "https://nothing.asnokaze.com/"
		},
		"type": "network-error",
		"url": "https://nothing.asnokaze.com/"
	}
]

動作確認

Chromeの「chrome://net-internals/#reporting」より、Network Error Loggingが有効であること、レポートとして送信されるデータが確認できる
f:id:ASnoKaze:20180528205145p:plain

一応、NEL確認用ページ
https://asnokaze.com/nel.html

エラーの種類

Network Error Loggingでは様々なエラーが定義されています。

  • ok エラーはない
  • dns.unreachable DNSサーバへ到達できない
  • dns.name_not_resolved DNSサーバは応答したが名前解決に失敗
  • dns.failedRequest 上記以外の理由で名前解決エラー
  • tcp.timed_out TCP接続タイムアウト
  • tcp.closed TCP接続がクローズされた
  • tcp.reset TCP接続がリセットされた
  • tcp.refused TCP接続が拒否された
  • tcp.aborted TCP接続が中断された
  • tcp.address_invalid TCP接続のアドレスが正しくない
  • tcp.address_unreachable TCP接続のアドレスへ到達できない
  • tcp.failed 上記以外の理由でTCP接続が失敗した
  • tls.version_or_cipher_mismatch TLSのバージョンや暗号が合わない
  • tls.bad_client_auth_cert クライアント認証が正しくない
  • tls.cert.name_invalid 証明書の名前が正しくない
  • tls.cert.date_invalid 証明書の有効期限が正しくない
  • tls.cert.authority_invalid 証明書のauthorityが正しくない
  • tls.cert.invalid それ以外の理由で証明書が正しくない
  • tls.cert.revoked 証明書が執行している
  • tls.cert.pinned_key_not_in_cert_chain key pinningのエラー
  • tls.protocol.error TLSプロトコルエラー
  • tls.failed それ以外の理由でTLS接続が失敗した
  • http.protocol.error HTTPプロトコルのエラー
  • http.response.invalid レスポンスが空やcontent-lengthが不一致、そのたユーザエージェントがレスポンスの処理を中断
  • http.response.redirect_loop リダイレクトループ
  • http.failed 上記の理由以外でHTTPエラー
  • abandoned ユーザによる中断
  • unknown 不明

今後

Blinkの開発者MLでChromeへの導入について議論している
https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/nNji_u7BRxo

このまま行けば、Chrome 68の安定版にも入ってくるだろう

GREASE for HTTP/2 の提案仕様

AkamaiのMike Bishop氏から、「GREASE for HTTP/2」という提案仕様が出ています。

HTTP/2では、未知のフレームタイプとSETTINGSパラメータは無視するようになっています。これは、将来HTTP/2を拡張できるようにするためで、実際に「RFC 8336 The ORIGIN HTTP/2 Frame」では新しくORIGINフレームを定義しています。新しい機能をつかっても、未対応な実装にはただ無視されるだけですので、利用しやすいです。

本来は未知のフレームやSETTINGSは無視すべきですが、未知のSETTINGSパラメータによって通信を終了する実装があるようです(URL)。このような実装は、将来の拡張を妨げる原因になります。

そこで、HTTP/2にもGREASEを導入するのが今回の提案です。また、HTTP over QUICにおいても同様の仕組みが盛り込まれるかもしれません。

GREASE for HTTP/2

GREASEとは

TLSにおけるGREASE、「Applying GREASE to TLS Extensibility」についての解説が詳しいです
jovi0608.hatenablog.com

ちゃんと、未知のパラメータが無視されるように、GREASE用の意味を持たないパラメータを送信します。これによって、日頃から未知のパラメータを受け取っても不具合を起こさず無視する実装であることを確認します。

こうすることで、実際にプロトコルを拡張する前、実装者は未知のパラメータを受け取ったときに不具合があれば気づけるようになります。

GREASE for Frame Types

0xb + (0x1f * N) のフレームタイプをGREASE用に予約します。このフレームはまったく意味を持っておらず、PADDINGの代わりに使用できます。

具体的には、0x2A (42), 0x49(73), 0x68(104).... などのフレームタイプ番号です。

GREASE for SETTINGS

"0x?a?a" のSETTINGSパラメータをGREASE用に予約します。このSETTINGSはまったく意味を持っていません。

具体的には、0x0a0a (2570), 0x1a1a(6682), 0x2a2a(10793).... などのSETTINGSパラメータです。