Fetchの仕様で application/report を送信時にCORSが必要になった

以前このブログでも書いたNetwork Error Loggingなど、Webサイトを表示する際に発生した問題のレポートを指定されたエンドポイントに送信する機能が増えてきている。
asnokaze.hatenablog.com

このレポート送信に関する仕様はReporting APIで定義されている。

一方で、Fetchの仕様としてCORSを送信する必要のないContent-Typeは下記の通りになっている 。見ての通り、レポート送信時に使用される content-type である

  • `application/csp-report` [CSP]
  • `application/report` [REPORTING]
  • `application/expect-ct-report+json` [EXPECT-CT]
  • `application/xss-auditor-report`
  • `application/ocsp-request` [OCSP]

この除外リストが出来るまでの議論はいぜん書いたとおり
asnokaze.hatenablog.com

Remove Reporting API from CORS exceptions

上記の除外リストから、application/reportを取り除くようである
github.com

そのため、Reporting API を使用している場合は、CORS対応を対応する必要が出てくる。Network Error Logging は対応必須である。一方で、report-uriディレクティブで個別にレポート先エンドポイントを指定してるCSPやExpect-CTは対応する必要はない。

実装

Reporting: Send CORS preflight before uploading reports」で、Chromeはすでに実装が入っている。

Chrome Dev(69)で実際にnelヘッダを試すとOPTIONSが飛んでくることが確認できた

2018/07/27 01:18:24 [debug] 4815#0: *86 http2 http request line: "OPTIONS /report HTTP/2.0"

Clear-Site-Dataヘッダでブラウザに記憶されているデータを消す

Webブラウザは表示したサイトに関する様々なデータを記憶しています

例えば

  • cookie
  • cache
  • HTTP認証の情報
  • localStorage
  • service worker registrations

しかしサーバ側からこれらのデータを明示的に消すのは難しい場合があります。例えば、httpOnlyのcookieは現在ブラウザにどのようなCookieがセットされているか知るのは難しいでしょう。

そこで、そのようなデータを消す Clear-Site-Data レスポンスヘッダW3Cで議論されています。実は、3年前に「Clear Site Data、ブラウザのローカルデータを削除する仕組み」で書いたときから微妙な変更が入っている。

また、ブラウザへの実装も進んでおり試せるような段階となってきている。

試す

こんな感じのヘッダを付けて、Chromecookieが消えるのを確認した

Clear-Site-Data: "cache", "cookies", "storage"

ちなみに仕様上指定できるのは

雑にCookieを設定する

f:id:ASnoKaze:20180724020335p:plain

消える

reloadしてClear-Site-Dataレスポンスヘッダを受け取ると消える。
executionContextsや、HTTP認証情報などは現状消えないが概ね動作している模様
f:id:ASnoKaze:20180724020714p:plain

ブラウザの廃止される機能を使っていることを検知する Deprecation Reports (+ Intervention reports )

20180713追記 Intervention reports について追記しました


ブラウザのバージョンアップは定期的に行われており、その度に廃止される機能もあります。

廃止されることは公式にアナウンスされますが、そもそも自分が使っているかどうかすべてを把握することは難しいでしょう。JavaScriptAPIであったり、HTTPヘッダだったりとその範囲は非常に広く、ライブラリの特定のバージョン以下だと問題になるといったケースも有るかと思います。

このような場合に役に立つ、廃止される機能が使用されていることをWebデベロッパー側で検知する仕組みが「Deprecation Reports」です。

Blink開発者メーリングリストでも議論されていますが、すでにChrome Canaryで動作するので、簡単に動作確認してみます。

Reporting API

表示してるページのエラーなどを指定したエンドポイントにレポートを送信させる、「Reporting API」という仕様があります。

以前このブログでも、以下の木の様を紹介します。

この仕様に新しく、Deprecation Reportsという機能が追加されました。これによって、廃止される機能を使っている場合に検知できるようになります。

ReportingObserver

その Deprecation Reports を、これまた新しく追加された ReportingObserver を使って検知してみようと思います。
ReportingObserver はReporting APIで定義されており、何かしらのレポートを送るような時にcallbackで任意の処理を実行できます(すべてのレポートタイプがObservableではない)。

こんな感じで、デベロッパーツールから実行する

function onReport(reports, observer) {
  for(let report of reports) {
    if (report.type == "deprecation") {
      console.log("message: " + report.body.message);
      console.log("url: " + report.url);
    }
  }
}

let observer = new ReportingObserver(onReport);
observer.observe();

上記に続けて、廃止される「webkitURL」変数を参照してみると、下記のようなデータが取得できる事がわかる

  • report.body.message として 'webkitURL' is deprecated. Please use 'URL' instead.
  • report.url として https://asnokaze.com/

(その他にも、reportのbodyから id, anticipatedRemoval, sourceFile, columnNumberなどが取れる)

あとはこの情報をどこかしらに送信するなりして集計すれば良い。このようにしてブラウザが将来廃止する機能を使っていることが事前に検知することが出来るようになる。

すでにChrome Canaryでは動作する。
f:id:ASnoKaze:20180706230905p:plain

Intervention reports

同じように、ChromeはIntervention reports にも対応しています。

下記のように、JavaScriptの書いたコードで仕様変更やセキュリティ/パフォーマンス上の理由により動作が変更された場合には、Reportタイプ Intervention がレポートされます。Deprecation Reports同様 ReportingObserverで検知できます。
qiita.com

「Intervention」についての詳細は、WICGのこちらから
github.com

TLS1.0, TLS1.1 の廃止する提案仕様

TLS1.3にRFC8446が採番されRFCとして出るのを待つばかりになっている。

一方で、「Deprecating TLSv1.0 and TLSv1.1」というTLS1.0とTLS1.1の廃止についての提案仕様がIETFで出ている。

TLS1.0, TLS1.1 廃止事情

カード情報セキュリティの国際統一基準 PCI DSS では2018年6月30日以降はTLS1.1を禁止している他、アメリカ国立標準技術研究所(NIST)もTLS1.2の移行を以前から推奨している。

実サービスにおいては、GithubAmazon ELBCloudFlareなどTLS1.0, TLS1.2の廃止を進めているサービスも多い。

また、IPAの出している「SSL/TLS暗号設定ガイドライン」でも、高セキュリティ型要件ではTLS1.2だけのサポートと書かれている。また「「SSL/TLS暗号設定ガイドライン改訂及び鍵管理ガイドライン作成のための調査・検討」報告書」でも各国・各標準化組織・各ベンダーの推奨事項への調査が報告されており、興味深い。

Deprecating TLSv1.0 and TLSv1.1

セキュリティ上の問題や、上記のような情勢を考慮してIETFでもTLS1.0とTLS1.1を廃止(historic)にしようというのが、「Deprecating TLSv1.0 and TLSv1.1」での提案です。

この提案では、古いバージョンのRFCをhistoricするとともに、TLSの利用に関する推奨事項を書いたRFC7525「Recommendations for Secure Use of TLS」でも、TLS1.0, TLS1.1の利用を禁止するように更新するものです。(現状では禁止はされていない)

ML上では活発に議論はされていないようですが、来月開催されるIETF102では何か動きがあるかもしれません。

(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とか)

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