はじめに
― toString() を破壊するとアプリはどうなるのか? ―
Prototype Pollution は「新しいプロパティをねじ込む」だけの可愛い攻撃ではありません。
油断していると、アプリケーションをまるごとクラッシュさせる DoS(サービス妨害)攻撃へ直行します。
今回の記事では、JavaScript アプリの心臓部分とも言える Object.prototype.toString() を狙い撃ちして、
アプリを落とすまでの流れを丁寧に解説していきます。
1. なぜ Prototype Pollution が DoS を引き起こすのか?
JavaScript のすべてのオブジェクトは Object.prototype を継承しています。
つまり、
Object.prototype.toStringを汚染したら、全オブジェクトの toString が壊れる
という、恐ろしく破壊的な現象が起こります。
toString() は以下のような場面で暗黙的に使われます:
- ログ出力
- 文字列テンプレート
${obj} - デバッグ
- オブジェクト比較
- JSON 変換時の内部処理
- フレームワーク内部のユーティリティ関数
ある意味、アプリ中で最も呼ばれる関数のひとつ。
そこを壊すということは――
アプリの“酸素”を奪うようなもの
です。
2. 典型的な攻撃の流れ
TryHackMe の教材で紹介されているケースをベースに見ていきましょう。
攻撃対象ページ(Clone Album)
<form action="/clone-album/1" method="post">
<input type="text" name="newAlbumName">
<button type="submit">Clone Album</button>
</form>
ユーザーが入力する newAlbumName は、
サーバ側で JSON としてパースされる可能性がある という点が致命傷でした。
そしてサーバ側では、例の脆弱な merge 関数が実行されます:
merge(clonedAlbum, payload);
3. toString を汚染する攻撃ペイロード
攻撃者が入力欄に次の JSON を入れます:
{"__proto__": {"toString": "Just crash the server"}}
これがサーバに送信されると…
-
JSON.parse()によってオブジェクト化 - merge() によって
__proto__.toStringが上書き - つまり Object.prototype.toString = "Just crash the server"` と同等
4. すると何が起こるのか?
アプリの内部では、至るところで toString() が呼ばれます。
ところが汚染後の toString は もはや関数ではなくただの文字列。
そのため、次のような例外が発生します:
TypeError: Object.prototype.toString.call is not a function
この瞬間、サーバの処理は止まり、
フロントには 500 エラーが表示され、ユーザーは正しく操作できません。
TryHackMe の教材でも、この動作が確認できます:
- 「Clone Album」をクリックすると即クラッシュ
- 画面はエラー画面に切り替わる
- サーバログにも TypeError が出力
Prototype Pollution の威力、侮れません。
5. 他に DoS を引き起こしうる汚染対象
toString は強力ですが、他にも DoS 化しやすいプロパティがあります:
| 汚染対象 | 影響 |
|---|---|
valueOf |
数値変換の失敗 → 数学処理全滅 |
toJSON |
JSON.stringify が壊れる |
constructor |
新しいオブジェクト生成が壊れる |
hasOwnProperty |
ほぼすべてのプロパティチェックがバグる |
__defineGetter__ / __defineSetter__
|
ループが無限再帰に陥る可能性 |
ただし すべてが即クラッシュするわけではない ため、
狙いどころとして最も安定して DoS に直結するのが toString() です。
6. 守るためにはどうするべきか?
開発者目線での対策はこちら。
① 危険キーを必ずブロック
if (["__proto__", "prototype", "constructor"].includes(key)) {
continue;
}
② JSON.parse を安易に使わない
ユーザー入力を文字列として扱うべき場面では 絶対に JSON 化しない。
③ マージ関数を自前で実装しない
lodash なども古いバージョンは Prototype Pollution の脆弱性がありました。
必ず 最新版を使用。
④ Object.freeze(Object.prototype)
場面によっては有効(ただし副作用も大きいので慎重に判断)。
まとめ
Prototype Pollution が危険な理由は、単に“変なプロパティが増える”だけではなく…
アプリ全体の基盤となる prototype が破壊され、
内部のあらゆる処理が正常に動かなくなる点にあります。
特に:
toString()valueOf()constructor
といった「全オブジェクトが頼る関数」を汚染されると、
アプリケーションは 一撃でクラッシュ します。