以下の内容はhttps://cysec148.hatenablog.com/entry/2025/09/17/055701より取得しました。


なぜObject.prototypeが書き換わってしまうのか

Hello there, ('ω')ノ

💥 問題のコード:1行ずつ分解

まず、改めて「マージ関数」と「渡すデータ」を確認します:

🔁 マージ関数

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];
    }
  }
}

🧪 ユーザー入力(JSON)

{
  "__proto__": {
    "isAdmin": true
  }
}

🔎 ステップバイステップで追ってみよう!

呼び出しはこうなっています:

merge({}, {
  "__proto__": {
    "isAdmin": true
  }
});

for (let key in source)

  • source{"__proto__": { "isAdmin": true }}
  • ループで key = "__proto__" が取り出されます。

typeof source[key] === 'object'

  • source["__proto__"]{ "isAdmin": true } というオブジェクトなので true
  • 次の処理へ進みます。

if (!target[key]) target[key] = {};

  • target["__proto__"] を確認。
  • 実は、ここで**target["__proto__"] にアクセスすること自体が危険**です。

🔴 この行の副作用:

target["__proto__"] = {};

この時点で、Object.prototype を空のオブジェクト {} に書き換えてしまいます。

なぜなら __proto__ というキーは、JavaScriptにとって特別で、オブジェクトの「親」を指しているからです。


merge(target["__proto__"], source["__proto__"])

  • 今度は {}{ "isAdmin": true } をまた merge() に渡します。

⑤ 次のループ

  • key = "isAdmin" が取り出され、
  • 最後の else に行きます:
target[key] = source[key]; // → {}["isAdmin"] = true
  • これにより、Object.prototype.isAdmin = true という状態が出来上がるわけです。

🧠 イメージ図

ステップ0: すべてのオブジェクト → Object.prototype → {}
                          ↑
ステップ1: __proto__ を通じて Object.prototype を上書き開始
                          ↓
ステップ2: isAdmin プロパティを追加(毒を盛る)
                          ↓
ステップ3: 以後すべてのオブジェクトに isAdmin が見える!

✅ まとめ(初心者向けの理解ポイント)

ポイント 解説
__proto__ オブジェクトの「親(プロトタイプ)」を指す特別なキー
target["__proto__"] = {...} 実は「グローバルなObject.prototype」を書き換える操作になる
すべてのオブジェクト Object.prototype を継承しているので、汚染の影響を受ける
初心者が混乱する点 target["__proto__"] を代入したつもりでも、実際はオブジェクト全体のプロトタイプを書き換えていること

Best regards, (^^ゞ




以上の内容はhttps://cysec148.hatenablog.com/entry/2025/09/17/055701より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14