Hello there, ('ω')ノ
🔁 危険な関数
まず、以下の関数は「2つのオブジェクトをマージ(結合)する処理」です:
function merge(target, source) { for (let key in source) { if (typeof source[key] === 'object') { if (!target[key]) target[key] = {}; merge(target[key], source[key]); } else { target[key] = source[key]; } } }
ここでは target に対して source を上書きしていきます。
再帰的に処理するため、source の中にオブジェクトがあれば深い階層までコピーされます。
🧪 ユーザー入力の例
この関数に次のような悪意のあるユーザー入力が渡されたとします:
{ "__proto__": { "isAdmin": true } }
これをJavaScriptで渡すと:
merge({}, JSON.parse('{"__proto__": {"isAdmin": true}}'));
⚠ なにが起こるのか?
JavaScriptでは、すべてのオブジェクトはObject.prototypeを継承しています。
そして __proto__ というプロパティを使うと、そのオブジェクトの「親」を書き換えることができるのです。
ここがポイント!
merge()は{ "__proto__": { "isAdmin": true } }をマージすると、target.__proto__ = { isAdmin: true }のように振る舞います。
つまり、最終的には Object.prototype.isAdmin = true と同じ状態になるのです。
🧨 その結果どうなる?
たとえば、以下のような空のオブジェクトを作っても:
let user = {}; console.log(user.isAdmin); // true が表示される!
userオブジェクトにはisAdminプロパティは存在しません。
でも、プロトタイプが汚染されているので、継承によって isAdmin が存在しているように見えるのです。
💡 図解で理解(イメージ)
[Object.prototype] ← { isAdmin: true } ← 書き換えられた親
↑
[user] ← 本来は何も持っていないオブジェクト
このように、「全てのオブジェクトの親」が書き換えられるため、新しく作ったどんなオブジェクトにも isAdmin: true が見えてしまうわけです。
✅ まとめ
| 項目 | 内容 |
|---|---|
__proto__ |
オブジェクトの親(プロトタイプ)を指定する特別なプロパティ |
| なぜ危険? | 書き換えると全オブジェクトに影響する |
merge()で発生する理由 |
再帰的にコピーする際、キーをフィルタしていないため __proto__ が素通りしてしまう |
| 結果 | Object.prototypeが汚染され、全てのオブジェクトに偽のプロパティが追加されたように見える |
Best regards, (^^ゞ