CDNのキャッシュを制御する CDN-Cache-Control ヘッダ

CDNのキャッシュを制御する「CDN-Cache-Control」を新しく定義する提案仕様「The CDN-Cache-Control HTTP Response Header Field」が出ているので、簡単に紹介する。

はじめに

HTTPではキャッシュを制御するのにCache-Controlヘッダを使用しますが、クライアントとは別にCDNに対して個別にキャッシュの制御を行いたい場合もあります。

その用途のために使用する「CDN-Cache-Control」を新しく定義しようというのが「The CDN-Cache-Control HTTP Response Header Field」です。

この仕様は、Akamai, Fastly, Cludflareに所属三人が共著で提出している点も興味深いところです。

CDN-Cache-Control

CDN-Cache-ControlヘッダのディレクティブはCache-Controlヘッダと同様です。

CDN-Cache-Controlがある場合は、Cache-control及びExpiresヘッダは無視されます。(CDN-Cache-Controlがなければ、Cache-controlが使用されます。)

例えば下記のようなレスポンスヘッダは

Cache-Control: max-age=60, s-maxage=120
CDN-Cache-Control: max-age=600
  • CDNでは600秒 レスポンスはfresh となる
  • CDN以外ではshared cacheは120秒、その他は60秒 fresh となる

例えばCDNではキャッシュを許可し、それ以外では保存させないという事もできます。

Cache-Control: no-store
CDN-Cache-Control: max-age=600

その他

提案仕様では、いくつかのFAQが書かれています。詳しくは原文を見てもらうとして、軽く紹介

なぜSurrogate-Controlを使わないのですか?

Surrogate-Controlヘッダーの実装はいくつかあるが、一貫性がないため、相互運用性の観点で新しいヘッダとして定義した

なぜCache-Controlヘッダに組み込まないのですか?

冗長性が減るが、相互運用上の観点で複雑性がますため

これはCDN専用ですか?

そうです。CDNゲートウェイキャッシュは区別すべきです。

複数CDNをつかってるんですが

複数CDN利用してる際にそれぞれ違うCASH制御したい場合があるかもしれません。

CDNは独自のヘッダ名を使うことが検討できます。

例えばFoo CDNでは「Foo-CDN-Cache-Control」を使うと言った風に。

POSTリクエストを冪等処理可能にするIdempotency-Keyヘッダの提案仕様

はじめに

HTTPリクエストには冪等なものと非冪等なものがあります。

仕様上、GETやOPTIONSは冪等であり、同じリクエストであれば何度行っても問題ありません。そのため通信上エラーが起こっても自動的にリトライすることが出来ます。

一方で、POSTリクエストは冪等ではありません。同じリクエストでも複数回行うと、結果が変わってしまいます。投稿や課金APIであれば2重に処理されてしまいます。

POSTリクエスト中にタイムアウトが発生した時に、サーバに処理される前にタイムアウトしたのか、サーバが処理したあとにレスポンスを返そうとしたところでタイムアウトしたのかクライアントは区別できません。そのため、POSTリクエストを一概にリトライすることは出来ません。

そこで、リトライにより複数回同じPOSTリクエストを受け取っても、同じものと識別できるように識別子をHTTPリクエストに付加できるようにする提案が行われています。

Idempotency-Keyヘッダ

PayPalのJayadeba Jena氏らに提出された「The Idempotency HTTP Header Field」では、冪等処理用の識別子としてIdempotency-Keyヘッダを定義しています。

   Idempotency-Key: "8e03978e-40d5-43e8-bc93-6894a57f9324"

クライアントはランダムな値(UUID)などを、ヘッダ値をセットします。

サーバはこのIdempotency-Keyを元に複数回同じリクエストを受け取っても冪等処理を行います。2回目以降のリクエストに対しては、初回と同じレスポンスを返します。

サーバは一定期間後に受け取ったIdempotency-Keyの値を忘却しても問題ありません。

Idea

この提案のアイデアは様々なところで似たような事が行われています。まさに、Idempotency-Keyという名前を使ってる他、リクエストIDという名前のものからあります。

参考までに、提案仕様の中で紹介されています

  • StripeのIdempotency-Keyヘッダ
  • PayPalPayPal-Request-Idヘッダ
  • Google Standard Payments APIのrequestId

などなど。。。

おまけ

余談ですが

HTTPのリトライ/リプレイに関しては、用語及び各実装の状況に整理している Mnot先生のドキュメントが学びになります

「Retrying HTTP Requests」

ホスト名の異なるTLS session resumptionについて

TLS1.3でホスト名が異なるTLS session resumptionを支援する拡張仕様が出ているので紹介する。

TLS1.3とセッション再開(session resumption)

RFC 8446 TLS 1.3 では、セッション再開(session resumption)という方法が定義されています。このsession resumptionを使うことで、2回目からのセッション確立を効率よく行えます。

このとき、TLS1.3からは異なるSNIを指定してのsession resumptionが出来るようになっています。

例えば、証明書が*.example.comの時に、a.example.comで確立したセッションを元に、b.example.comに対してsession resumptionを行えます。

しかし、一般に証明書がカバーしているホスト名だとしてもセッション再開のための状態がサーバ間で共有されているとは限りません。うまく行かないのにクライアントはセッション再開を試みるのは、セッションチケットを無駄にしてしまいます。

そこで、サーバから「この証明書がカバーするホスト名では、SNIの異なるsession resumptionをして良い」と明示する拡張仕様が検討されています。

TLS Resumption across Server Names

TLS Resumption across Server Names」は、GoogleのVictor Vasiliev氏によって提案されている仕様です。

この仕様では、NewSessionTicketメッセージの拡張としてresumption_across_namesを定義しています。

このresumption_across_names拡張をクライアントに通知することで、「この証明書がカバーするホスト名では、SNIの異なるsession resumptionをして良い」ということを示します。

パフォーマンス

Victor Vasiliev氏らの提案とは関係なく、2019年にハンブルク大学のErik Sy氏らによって「Enhanced Performance for the encrypted Web through TLS Resumption across Hostnames(pdf)」という論文が公開されています。

この論文では、有名サイト閲覧時、異なるホスト名でセッション再開した場合の効率性を調査しています。

調査によると、Web上のサイトを取得する際に、行われるTLSハンドシェイクのうち約6割ほどがホスト名の異なるセッション再開が行えるとのことです。

これにより、セッション確立にさかれる時間を最大30%早く出来るとのことです。

NTPの次期バージョンv5の標準化が始まる

NTPの次期バージョンとなるv5のドラフト(草案)が出たので目を通す。

はじめに

2010年にRFC 5905としてNTPv4が標準化されています。それから10年たち、IETFのNTP WGではNTPv4の問題点を整理ながら、NTPv5をどのようにするか議論してきました。

そして11月23日にRed HatのMiroslav Lichvar氏から最初の草案となる「Network Time Protocol Version 5」が提出されました。

下記の4つのタイムスタンプを元に時刻同期を行うというところは大きく変わってはいません

  • クライアントがパケットを送った時間
  • サーバがパケットを受信した時間
  • サーバがパケットを送信した時間
  • クライアントがパケットを受信した時間

一方細かい改善で高精度化や、うるう秒対策で使われるleap smearの対応などがあります。そういったNTPv4との違いについてかんたんに眺めていきます。

ただし、最初期案のため、ここから変更が入ると思われます。

NTPv5

Clientモード/Serverモードのみサポート

NTPv4はAssociation Modeとして6つが定義されていました

  • Symmetric Active
  • Symmetric Passive
  • Client
  • Server
  • Broadcast Server
  • Broadcast Client

NTPv5では、ClientとServerのみのサポートになりました

UTC以外のtimescaleに対応 (leap smearもあるよ!)

NTPではUTCで時刻を指定していましたが、v5では下記の4つが選択できるようになりました

  • UTC
  • TAI
  • UT1
  • Leap-smeared UTC

特に、昨今よくつかわれているLeap-smeared(うるう秒を1日かけて調整する方式)を指定できるようになっています。こうすることで実装同士がLeap-smearedされていることを認識できるようになりますね。

アルゴリズムの記述を削除

NTPv4の仕様は「Network Time Protocol Version 4: Protocol and Algorithms Specification」でしたが、v5では「Network Time Protocol Version 5」となっています。

この通り、サーバと同期するのに適切化フィルタするアルゴリズム(Clock Filter Algorithm)らへんの記述はなくなりました

NTPv4では、ユーザランドでパケット送信処理を行ったあとに、低レイヤ(OS/ハードウェア)から得られるより正確な時刻情報を同期に使えるようにする「NTP Interleaved Modes」という仕様が存在しています。

これをNTPv5では、これを行うためのフラグ及びCookieを持ちます

Reference IDsの改善

NTPサーバは上位のサーバを示すために、32bitReference IDフィールドに上位サーバのIPアドレスを格納します。

この値はループ防止に使用されていましたが、アドレス変換などがあると適切に扱えていませんでした。

そのためReference IDsも改善される予定です

32-bit 秒フィールドの高精度化

NTPには、Root Delayといった32-bitで秒を示すフィールドがあります。

NTPv4では16bitを整数部・16bitを小数部として秒を表現していましたが、この場合15ミリ秒の精度になってしまいます。

NTPv5では4bitを整数部・28bitを小数部として秒を表現することで、約3.7ナノ秒の精度で表現可能となっています。

拡張

MACなどが拡張フィールドとなっていたりしている。現在NTPv5で定義されている拡張フィールドは下記の通り

  • Padding Extension Field
  • MAC Extension Field
  • Reference IDs Extension Field
  • Correction Extension Field
  • Reference timestamp
  • Monotonic Timestamp Extension Field

また、拡張フィールドは任意長になった (NTPv4だと4オクテットの倍数)

パケットフォーマット

f:id:ASnoKaze:20201115231335p:plain

(拡張フィールドは任意長かつ、任意回繰り返し)

eraもパケットに入るため、タイムスタンプ32bit整数部で表現できる136年✕256(8bit)で約35000年の範囲を表すことができます。

おわりに

まだたたき台が出てきたところで、これからどんどん変わると思われるので注意。

ひとまず、来週あるIETF109でどのような議論が行われるか注目していきたい。

ChromeのHTTP/2サーバプッシュサポート廃止検討と、103 Early Hintsについて

ChromeがHTTP/2サーバプッシュのサポートを廃止を検討し始めている。またPreload + 103 Early Hintsの有効性について実験していく模様

背景

HTTP/2(RFC 7540) にはサーバプッシュという機能があります。これは、クライアントからのHTTPリクエストをまたずにサーバがHTTPレスポンスを先行して送ることが出来る機能です。

たとえば、index.htmlに画像1,2,3が含まれているようなページがあるとします。このindex.htmlへのリクエストを受け付けたサーバは、クライアントが画像1,2,3がを要求してくることを予測しサーバプッシュでこれらのリソースを送りつけることが来でます。(クライアント側から、そのリソースが不要であればサーバプッシュをキャンセルすることもできます)

f:id:ASnoKaze:20201113000937p:plain

そうすることで、ページの表示を効率化出来ると考えられていました。

しかし、様々な議論の中で、クライアントがすでにキャッシュを持っているケース、優先制御上デメリットがあるなどの様子が見えてきました。そういった話もありつつ、HTTP/2サーバプッシュは広くは使われていないのが現状です。

そんななか、Chromeの開発者のメーリングリストblink-devで「Intent to Remove: HTTP/2 and gQUIC server push」という投稿がなされました。このメールでは、HTTP/2の現状を鑑みてサーバプッシュのサポートを廃止すること、103 Early Hintsの効果測定をしていく旨が書かれています。

Intent to Remove: HTTP/2 and gQUIC server push

現状

過去28日間で、HTTP/2コネクションでサーバプッシュを受け取った割合は0.05%ほどになっています。
Firefoxでも同じぐらいの数値であることが共有されています)

また、パフォーマンスとしてはAkamaiがあまりかわらないか、特定環境でわずかに向上するという資料が共有されています(リンク)

変更内容

ChromeはHTTP/2とgQUIC(Google版QUIC)でサーバプッシュをサポートしています。なおHTTP/3も仕様上はサーバプッシュ機能を持ちますが、Chromeはサポート予定が無いとのこと。

HTTP/2では通信中にSETTINGSフレームで、サーバプッシュを無効にできますので、Chromeはそれを送信するように変更されます。

Prelaod と103 Early Hints

サーバから突然リソースを送りつけるサーバプッシュではなく、Preload方式を取る方向が検討されています。Preloadでは、必要なリソースURLだけをクライアントに通知し実際にそのリソースを取得するか(キャッシュを利用するか)はクライアントが判断します。クライアントは取得するばあいは通常通りのHTTPリクエストを送信します。

Preloadの指示の仕方は「Preload」で書かれています。

たとえば、index.htmlの要求にするHTTPレスポンスをする際に、styles.cssもプリロードしといてねという風に伝えるには下記のようなレスポンスヘッダをつけます。。

Link: <https://example.com/other/styles.css>; rel=preload; as=style

このPrelaod指示をどれだけ早くクライアントに通知するかの工夫の一環で、2017年に
RFC 8297で「103 Early Hints」という新しい仕組みが標準化されています。

100番台のステータスコードは、継続処理を示すもので、本当の200や404レスポンスよりも前にレスポンスすることが出来ます (100番台のあとには最終的なレスポンスを返す必要があります。)
f:id:ASnoKaze:20201112235610p:plain

一般的にステータスコードが決定するのはHTTPリクエストを完全に処理し終わったあとです。ですので重たいサーバ側処理が完了しないと、いかなるレスポンスヘッダも送り始めることは出来ません(100番台レスポンスを除く)。

そういったケースで、最初の103レスポンス時にLinkヘッダでPreload指示を送ればより早いタイミングでクライアントにPreloadを指示できるようになります。

この103 Early Hintsの有効性について、ChromeとFastlyが共同で実験していくというのがちょうど昨今の動きになります。
www.fastly.com

NAT Slipstreaming攻撃とブラウザ側の対策

2020年10月31日に「NAT Slipstreaming」という攻撃手法が発見されてます
samy.pl

これは簡単に言うと

罠サイトを踏ませることで、SIPのApplication Level Gateway機能を持つNATの内側に居るクライアントに対して、外側からそのクライアントの任意のTCP/UDPポートに接続できる。という攻撃のようです。

この攻撃はさまざなテクニックを使用しており大変面白いです。調査過程も含め詳細は上記のサイトに書かれているので、そちらを読むことを強く推奨します。

ざっくり

登場人物

  • victim(攻撃対象): ブラウザで攻撃者のサイトにアクセスすr
  • attacker (攻撃者): 罠サイトを準備し、最終的にNAT内側のvictimの任意ポートにアクセスする
  • Application Level Gateway NAT: NATの機能を持つが、5060ポートで送信されるSIPメッセージを見つけると、そのSIPメッセージ内に含まれるIP/Portもちゃんと外向きに書き換え、マッピングを行い通信を中継する。

f:id:ASnoKaze:20201107225622p:plain
(解説サイトより引用)

  • 1. victimが、直接もしくは広告を通してattackerのサイトを訪れます
  • 2. attackerはWebRTCやタイミング攻撃を通してvictimのinternal IPを取得します(後のSIPメッセージを構築時に使用します)
  • 3. attackerは、Hidden Formを通して大きなデータを送信させ victim のIPスタックの最大MTUサイズを検出します(後のSIPメッセージをパケット境界に合わせるために使用します)
  • 4. attackerは、Hidden Formを通してvictimに下記のようなPOSTをport5060でattackerサイト宛に送信させます。HTTPボディにはSIPのRegisterメッセージが格納されていますが、このSIPメッセージがちょうどパケット境界になるように途中にデータを埋め込み調整します。(このSIPメッセージのContactヘッダが最終的に攻撃者が通信を行いたい先です)
POST / HTTP/1.1
Host: samy.pl:5060
Connection: keep-alive
Content-Length: 191
Cache-Control: max-age=0
Origin: http://samy.pl

(中略)

------WebKitFormBoundaryhcoAd2iSAx3TJA7A
Content-Disposition: form-data; name="textname"

REGISTER sip:samy.pl;transport=TCP SIP/2.0
Contact: <sip:samy@192.168.0.109:1234;transport=TCP>


------WebKitFormBoundaryhcoAd2iSAx3TJA7A--
  • 5. 上記のSIPメッセージを観測したApplication Level Gatewayは、SIPメッセージの内部IPをグローバルIPに変更しつつ、そのIP/Portをマッピングして中継するようになります。(パケット境界にあわせると、SIPのパケットとして処理されてしまう模様)
  • 6. attackerは受け取ったregisterパケットに含まれるIPとポートに接続することで、NATの内側のvictimの任意のポート(AttackerがRegisterメッセージに埋め込んだポート)に接続できます

UDPでNAT内部アクセスしたい場合は、WebRTC TURNをつかってSIPメッセージを偽装する)

ブラウザの対策

これはブラウザ側の脆弱性というわけではないが、ワークアラウンドとして対策されている。

具体的には5060/5061のポートへのリクエストはブラウザによってブロックされる変更が入っている。

同様に、whatwgの仕様上もブロックポートに加えられている
https://github.com/whatwg/fetch/pull/1109

「RFC7525: TLSを安全に使うための推奨事項」の改定

様々な組織や団体がTLSを安全に利用するためのドキュメントを出してたりする。

IETFでも2015年にRFC7525 「Recommendations for Secure Use of TLS and DTLS」として、使用する上での推奨事項をまとめている。

それについての詳細は、urushima先生の記事を見ていただくと良いと思う。
blog.livedoor.jp

上記のRFC7525から5年が経ち、その間にRFC 8446 TLS1.3の発行などもありました。そのため、IETFではこのRFC7525の改定作業が開始されました

RFC7525bis

IETFUTA (Using TLS in Applications) WGでは、RFC7525の改訂版として、次のドキュメントをすでにWG Itemとして扱っている

ここでは、簡単にRFC7525からの変更点について紹介する

  • TLS1.0とTLS1.1の利用を"SHOULD NOT"から"MUST NOT"に変更
  • TLS1.3/DTLS1.3の利用をSHOULD で追加
  • TLS1.2ではSCSVの実装がMUSTに
  • TLS Encrypted Client Helloへの言及を追加

まだ、Draft 00 ということもあって、差分もあっさり目。