mizdra's blog

ぽよぐらみんぐ

ある Web ページに関するリソースのうち、ブラウザにリークしているものを調べる

最近の Web フロントエンドの開発では、JavaScript/CSS ファイルを bundler (webpack, vite, ...) でバンドルして、それをブラウザに配信することが多いと思います。

例えば以下のようなコードを bundler でバンドルすると、react + react-dom/client + ./locales/ja-JP.json が結合された bundle.js というファイルが生成されます (出力されるファイル名は bundler によって異なるのでイメージです)。

import React from 'react';
import { createRoot } from 'react-dom/client';
import locale from './locales/ja-JP.json';

function App() {
  return <div>{locale['Hello World!']}</div>; // => 'ようこそ、世界へ!'
}

const root = createRoot(document.getElementById('app')!);
root.render(<App />);

一方で、 bundler はモジュールから辿れるものを片っ端からバンドルするので、時として「本当はバンドルされて欲しくないもの」までバンドルされてしまうことがあります。例えば ./locales/ja-JP.json に入っている文言に未解禁情報が含まれていて、それが bundle.js に含まれてしまい、ユーザにリークしてしまう… そんな状況が考えられます。

ブラウザにリークしているリソースを調べる方法

どうコードを書き換えたらリークを防げるのか、リークを未然に防ぐ仕組みはどうやったら作れるか、などこのテーマについて話せることは色々ありますが、今回は「ブラウザにリークしているリソースを調べる方法」について紹介します。

bundler の成果物を grep する

一番シンプルです。./dist 配下などを未公開情報をキーワードにして grep するだけです。

./dist 配下が全てブラウザに配信される」ことが分かっているのならこの手法で良いですが、場合によっては「./dist 配下にサーバーサイドで使われるものとブラウザに配信されるものが混在していて、単純に grep できない」こともあるかもしれません。bundler の出力先のディレクトリ構成を見直して grep しやすい形に整理したり、あるいは他の手法で調べたりすると良いと思います。

ちなみに Next.js は .next 配下 にサーバーサイドで使われるリソースとブラウザに配信されるリソースが混在している典型的な例ですが、このうちブラウザに配信されるリソースは .next/static 配下に集められています (ただし undocumented な挙動なので今後変わりうるかも)。Next.js なら .next/static 配下に対して grep すれば良さそうです。

この記事ではあまり深入りしませんが、CI 上で next build 後に .next/static 配下を grep すれば、デプロイ前にリークに気づけるような仕組みを作れます。興味があれば試してみてください。

Chrome devtools の Network パネルの検索機能を使う

Chrome devtools の Network パネルを使うと、ページ表示中に発生した全てのネットワークリクエストを覗き見ることができます。実はこの Network パネルには検索機能がついており、ページ表示中に発生した全てのネットワークリクエストを対象に、平文検索を実行できます。これを使うと、未公開情報のリークの有無を確認できます。

  1. Network パネルを開いて Command + F キーを押す (macOS の場合 / 他の OS でも検索ショートカット相当のやつでいけるはず)
    • 検索欄が出てくるはず
  2. 検索欄にリークの有無を確認したいキーワードを入力
    • 検索結果に結果が表示されればリークしてる
  3. 検索結果をクリックして、ファイルを開く
    • キーワードが埋め込まれたファイルの中身が表示される
    • minify されてるファイルだと読みづらいかも
      • 左下の {} ボタンを押すと、ファイルがフォーマットされてちょっと読みやすくなる

Network パネルを使ったリークの有無の確認方法の図解

一点注意しておきたいこととして、実際にページ表示中にブラウザで発生したネットワークリクエストのみを対象に検索するため、リクエストされなかったリソースは検索対象に含まれません。つまり、実行時に特定の条件に応じて fetch でリソースを DL してきて、それを表示するようなコンテンツがあった際に、そのコンテンツに非公開情報が含まれていても、ヒットしない可能性があります。ヒットしないものの、bundle.js 中にはそのリソースの URL が埋め込まれているはずで、ユーザその URL を直接アドレスバーに打ち込んでアクセスすれば、非公開情報を閲覧できてしまいます。

クライアントサイドで動的に出し分けているリソースについては、この手法ではリークの有無を完全に確認出来るわけではない、ということに気をつけましょう。


他にもリークの有無を確認する便利な方法があれば教えて下さい!

ポケットモンスター・ポケモン・Pokémon・は任天堂・クリーチャーズ・ゲームフリークの登録商標です.

当ブログは @mizdra 個人により運営されており, 株式会社ポケモン及びその関連会社とは一切関係ありません.