Hello there, ('ω')ノ
🧨 攻撃に使われる悪意あるJSON
攻撃者が以下のようなJSONを送信したとします:
{ "__proto__": { "evilProperty": "payload" } }
例えば、WebSocketのメッセージやAJAXリクエストなどでサーバやフロントエンドに送信されることがあります。
🤖 JavaScriptでどうなる?
これを JSON.parse() でパースすると:
const objectFromJson = JSON.parse('{"__proto__": {"evilProperty": "payload"}}');
このとき objectFromJson の中身は:
{ "__proto__": { "evilProperty": "payload" } }
つまり、proto は文字列としてそのままプロパティになるということ。
💡 なにが面白いのか?比較してみよう
const literalObj = { __proto__: { evilProperty: 'payload' } }; const jsonObj = JSON.parse('{"__proto__": {"evilProperty": "payload"}}'); console.log(literalObj.hasOwnProperty('__proto__')); // false(プロトタイプを書き換えた) console.log(jsonObj.hasOwnProperty('__proto__')); // true(単なるキーとして扱われた)
この結果からわかること:
| 作り方 | __proto__の扱い |
|---|---|
オブジェクトリテラル {} |
特別なキーとしてプロトタイプを変更 |
JSON.parse() |
普通のプロパティとして扱われる |
ここまでは安全そうに見えるかもしれません。
😱 問題は「その後のマージ処理」にある!
次のようにマージされると危険です:
merge(target, JSON.parse('{"__proto__": {"evilProperty": "payload"}}'));
この merge() 関数が以前紹介したような再帰的マージだった場合、次のような操作が実行されてしまいます:
target.__proto__.evilProperty = 'payload';
つまり、結局 Object.prototype.evilProperty = "payload" という形でプロトタイプが汚染されるのです。
💥 なぜ危険なのか?再確認
JSON.parse()自体は安全に見える- しかし、その後の処理(マージ)が危険
- 結果的に、全てのオブジェクトが
evilPropertyを継承してしまう
🧠 理解のポイント
| ポイント | 解説 |
|---|---|
JSON.parse() |
入力に含まれる__proto__を単なる文字列キーとして扱う |
| 問題点 | それを merge() などでマージするとプロトタイプに代入されてしまう |
| 被害 | 全オブジェクトに evilProperty が見えるようになる |
| 攻撃対象 | アプリやライブラリが参照する重要プロパティ(例:isAdmin)など |
✅ 対策まとめ
__proto__,constructor,prototypeのような危険なキーをブラックリスト化- サニタイズ処理を必ず実装する(入力前/マージ前)
- 安全なマージライブラリを使う(例:
deepmergeなど) - JSONから生成したオブジェクトはそのまま信頼しない
🔚 おわりに
JSON.parse() は一見安全に見えますが、その後の処理次第で重大なセキュリティリスクにつながります。特に、ユーザーから受け取ったデータを他のオブジェクトに「マージ」するようなコードには十分注意が必要です。
Best regards, (^^ゞ