Cache Storage がめちゃくちゃ肥大化する問題について調べる

先に結論からいうと実サイズとは全然違う。

Cache Storageがめちゃくちゃ肥大化する問題

TBSのニュースサイト、TBS NEWS DIGがめちゃくちゃブラウザのストレージを消費しているという話がはてブや増田で話題になっています。

TBSのニュースサイトヤバない? – はてな匿名ダイアリー

同・はてなブックマーク

確かに、手元でも同様の状況を観測できる。

当該サイトのストレージ使用状況

はたして、これは真実なのだろうか。本当に1.4GBも食うことがあるのだろうか……

そんなわけない、ということで調査

まずは再現性を確認するためにChromeのゲストモードで当該のサイトのDevtoolを開いてましょう。すると、StorageのUsageは386MBになっていました。(適当なページを開き、リロードした時点で340MB程度であった)

当該サイトのストレージ割合

上記のスクリーンショットをよく見ていただけるとわかると思いますが、このほとんどはCache storageが占めているのです。

元増田では

Cookie(LocalStorageとかも含むのか知らんけど)

https://anond.hatelabo.jp/20230426221025

となっていますが、今回問題なのはどちらでもなくCache storage。ちなみにLocalStorageは5MiBに制限されています

Cache storageとは、PWAの文脈ではおなじみのServiceWorkerでブラウザのリクエストを中継し、このレイヤーでレスポンスをキャッシュ管理するために使うものです。(がばがばな説明)

当該サイトではGoogleのWorkboxというこれまたおなじみのライブラリが使用されています。

では、Cache storageの中身を見てみます。

Cache storageには記事内の画像などのmwcache-mwimages、そしてスクリプトや各種リソースなどのmwcache-resourcesといった2種類のキャッシュが存在しています。

しかし、これらのキャッシュを構成するResponseはいずれも大きくて数百KB、大半のものは10KBにも満たしません。つまり、これらを足したところで数百MBはおろか、10MBにも満たないことが分かります。

ではここにないものが計上されている?とも考えられるかもしれませんが、そういうわけでもないのです。

試しに消してみると、何かがおかしい

スクショをよく見てもらうと、Response-Type という項目が opaquebasic の2種類あることがわかるかと思います。詳しい説明は後でするのですが、これが今回の問題のカギになります。

では、ここからこれがOpaqueとなっているものを1つ削除してみましょう。適当に1つ選択してDel または左上の×を押すだけです。

なんと、数KBのファイルを消しただけなのに5MBも減りました。

今回は5MBでしたが、7MBを中心に1MBだったり12MBだったりするようです。なぜ?

実はこれ、セキュリティ上のChromiumの実装

この問題について調べていると、あるStackOverflowのQ&Aにたどり着きます。詳しくまとまっている。

What limitations apply to opaque responses?

さきほどのOpaque Responseについての質問です。

Opaque、不透明なレスポンスというのは、クロスオリジンかつCORSでない、本来JS、ServiceWorkerからアクセスできなりリソースのレスポンスを指します。

ServiceWorkerはブラウザのレンダラに代わって(仲介して)リクエストをやりとりするわけなので中身は読み取れずとも、そういったリクエストを扱うことができます。

この質問では、当該するリソースの例として次のようなものが挙げられています。

  • <script>
  • <link rel="stylesheet">
  • <img><video>, and <audio>
  • <object> and <embed>
  • <iframe>
What limitations apply to opaque responses?

ブラウザのストレージの使用量を取得するAPI navigator.storage.estimate() は、こういったリソースについて不正確なPaddingをもった値を報告します。

SWの初期の初期の実装では、これは正確な値を返すように実装されていたようなのですが、Chromeの63あたりの特定のバージョン以降はPaddingを盛った値を報告するようになったというのです。これは以下のバグトラッカーの通り。

796060 – Cache Storage value rises on each refresh when Analytics code is in the html – chromium

セキュリティ上の理由から、Paddingを盛っているとのこと。

セキュリティ?どういう問題?

ここからは完全に推測に過ぎないわけですが、ファイル長からその中身が推定できると仮定すると、悪意のあるページで悪用が可能になる気がします。パスワード推定にそういったのを使う話もみかけますが、たとえばこれ、対策のガバい特定のサイトにユーザーがログインしているかどうかだって判定できる可能性があるのでは。。。?このあたりはセキュリティ詳しい方お願いします。

で、実際のディスク上のサイズは?

Cache storageの実体は当然Chromeのプロファイル内に存在します。

Chromeのプロファイル以下、 Service Worker/CacheStorage に実体がありました。

たったの5MB前後!!!!!(Chromeでは1.4GBと出てる)

まとめ

  • Chromeが報告するサイトごとのストレージ使用量は大嘘
  • SWに通知するストレージ使用料はPaddingが1resあたり7MBほど乗ってる
  • とはいえ設定UIにまでその値を使わないでほしい

参考

Storage Standard

What limitations apply to opaque responses?

796060 – Cache Storage value rises on each refresh when Analytics code is in the html – chromium


P. S.

なんかOpaqueじゃないレスポンスについても肥大化する報告があるらしい。YouTubeとかで発生している(YouTubeはすべてbasic)らしいので、もしかするとほかで同様の問題があるのはこっちの場合もあるかもしれない。


元増田が更新されてたので追記04/28

元増田が追記していたのでそのあたりについても思うことを書いておこうと思う。

まずは、元増田が細かいことわかる人で、最新のコードレベルで詳しいとこまで深堀をしてくれている。7MBの理屈とかもそうそうってかんじのことをわかりやすく書いてくれてる、気がする。

で、その内容について軽く触れておく(もう誰も気にしてないと思うが)

  • ApplicationタブでCacheStorageの各アイテムのResponseヘッダのexpiresを見ると概ね1か月後が指定されている
    • expiresは1月後になっているが、sw.jsを見ると画像は1週間を期限にCache Storageからは削除するように設定していることができる。(失効タイミングは仕様上厳密ではない) 詳しくはworkboxのドキュメント
      • なお、この期限はIndexedDBで別途管理されている
  • 開いてもいない記事の画像がCacheStorageに格納されている(スクロールすると下の方に載ってる)
    • そもそも開いたか開いてないかにかかわらず、こういったちまちました動的アセットをSWでキャッシュする必要があるかはたしかに疑問。ブラウザキャッシュに任せておけばいいと思うんだけど。
    • 別に先読みとかしているわけではなく、ページ内に小さなサムネがいっぱいあるのでそれを読み込むとそのままキャッシュ行きしている。一応スクロールに応じた遅延読み込みはしているみたい。
  • Networkタブで確認すると、ページのロードが一見完了したように見えても延々と動画の断片や画像らしきデータが転送され続けている
    • これこそが遅延読み込みであり、どうやら当該サイトではlazed.jsが使用されているらしい。

先読みをしているかというと、していないように見える。sw.jsにもそういったコードはなく、あくまでもfetchしたものをstoreしている感じ。

  • TBS等の特定のサイトだけデカくみえる理由の説明にはならないのです
    • かいたとおもって書いていなかったみたいだが、たいていのサイトは同一オリジンで完結しているので(そもそもフロントもCDN通してるでしょ?)Opaqueなリクエストはさほど増えないんじゃないかと思っている。あってもせいぜい少しであれば、大したサイズにはならないので気になっていなかったといえないだろうか。もっとも、何度も言うように画像を全部Cache Storageで管理する必要性は感じない。
    • そもそも、swでCache Storageを使っているサイトなんでアプリでもなければほんの一握りな気もする。ServiceWorkerを使用していてもCacheを自前で管理しなければいけないわけではない。もちろん、オフラインアクセスには有効なのでオプトインさせることもある。

曖昧な憶測をの根拠を固めるべくく、手元のキャッシュからサンプルを集めている中、「キャッシュは大した数ないことになっているのに(たとえば、Opaqueredirectが5つくらい)、120MBって出てる」「Opaqueなのは1つもないのに600MBになっている」という状況も見られた。しかし、これらはswで一度キャッシュされたものが残っている状況であった。swを停止や再起動すると、それらは一度開放される。もっとも、ディスク上に残り続けているかは確認できていないが。

そのうえで、SWの有効なサイトいくつかに対してあまり大きくなっていないサイトをか確認したら、たいていはそもそもキャッシュを自前で管理していなかった。していても、同一オリジンでたいていが完結しているので当該の問題は顕在化していない。


そういえば、はてなのGigaViewerを使用しているサイトにおいても、このあたりにはいくつかのバリエーションがみれる。おもしろい。

たとえば、以下の4サイト(これはおもいついたもの)において3種類のバリエーションがある。

  • 少年ジャンププラス
  • となりのヤングジャンプ
  • マガポケ
  • くらげバンチ

いずれもログイン等していない状態で、

  • そもそもswが起動していない
  • SWは起動しているがキャッシュしているのは1ファイルのみ
  • SWが起動しており、サムネ等のキャッシュをしていいるが例のPadding爆発が発生している

といったパターンが見受けられる。ところで3つ目のパターンについては空のCacheが大量に増殖する状況にあるのではてなの中の人~~~~~

きになる方は自分の目で確かめてほしい。


しょうもないエントリですが、最後まで読んでくれてありがとうございます!私を支援できます:


ホッテントリトップありがとう!!!!!!

コメントを残す