セキュリティ対策
プロトタイプ汚染(Node.js)— 本体が「脆弱性ではない」と定めた領域は、自分で守るしかない
JSONに紛れた特殊なキーが再帰的マージを通ると、設定した覚えのないプロパティがオブジェクト全体に現れます。Node.js公式は『利用者入力に依存するプロトタイプ汚染はNode.jsコアの脆弱性とは見なさない』と明記しつつ、アプリと依存の層で防げと求めています。つまり直してくれる人はいません。仕組みと、公式が挙げる7つの緩和策を防御目線で解説します(CWE-1321)。
外部から届いたデータを取り込んだあと、設定した覚えのないプロパティが、無関係なオブジェクトにまで現れる。あるいは、hasOwnProperty のような標準メソッドが「関数ではない」と言われてアプリが落ちる。どちらもプロトタイプ汚染の症状です。
まず押さえるべきは、責任の境界
Node.js は「これは自分の脆弱性ではない」と明示している
Node.js公式のセキュリティ指針は、この類型について明確です——「Node.jsの脅威モデルでは、攻撃者が利用者入力を制御することに依存するプロトタイプ汚染は、Node.jsコアの脆弱性とは見なされない。Node.jsはアプリケーションコードが与える入力を信頼するためである」。
そして、そのまま続けてこう書かれています——「とはいえプロトタイプ汚染はNode.jsアプリケーションおよびサードパーティライブラリにとって深刻な脆弱性のクラスであり、アプリケーションと依存関係の層で防御を実装すべきである」。
これは矛盾ではなく、境界線の宣言です。ランタイムは「入力の正しさ」を保証しない側に立っている。だからCVEを待つ・アップデートで直るのを待つ、という戦略はここでは機能しません。当サイトが繰り返し書いていることの、また別の実例です——誰かが守ってくれていると思っている場所に、実は誰もいない。
何が起きるのか(2種類の結果)
外部入力(JSON・クエリ・フォーム)
特殊なキーが混じっている
↓ 再帰的マージ/深いコピー/設定の合成
共通のひな型(プロトタイプ)が書き換わる
↓ 同じひな型を継ぐ、すべてのオブジェクトへ
① 覚えのない値が現れる
権限フラグやオプションの判定が狂う
② 標準メソッドが消える
無関係な場所でアプリが落ちる(DoS)
- ① プロパティの注入
- 「設定していないはずの値」が読めてしまう状態になります。オプションオブジェクトや権限フラグを 存在チェックだけで判定している箇所は、注入された値をそのまま信じます。認可の判定に効けば権限の話になり、テンプレートやコマンド組み立てに効けばさらに深刻な話になり得ます
- ② サービス停止(DoS)
- ひな型そのものを壊すと、
hasOwnPropertyのような組み込みメソッドが「関数ではない」というエラーになります。公式ドキュメントもこれを潜在的なDoSとして示しています。権限昇格だけの問題ではないという点が重要です - 実例の範囲
- 公式が挙げている例は Node.js 本体(CVE-2022-21824) と サードパーティライブラリ(Lodash の CVE-2018-3721) の両方。自分が再帰的マージを書いていなくても、依存が書いていれば同じことです
公式が挙げる緩和策(7つ)
Node.js公式は緩和策を具体的に列挙しています。入口で止めるものと、データ構造そのものを汚れない形にするもの、読み方を変えるものに分けて理解すると使いやすくなります。
入口で止める
・安全でない再帰的マージを避ける(公式は CVE-2018-16487 を例示)
・外部・非信頼のリクエストに JSONスキーマ検証を実装する
想定外のキーを最初から通さないのが最も確実です。深いオブジェクトを丸ごと受け取って合成する設計そのものを見直してください。
構造と読み方を変える
・Object.create(null) でプロトタイプを持たないオブジェクトを作る(辞書用途に有効)
・Object.freeze(MyObject.prototype) でプロトタイプを凍結する
・Node.js の --disable-proto フラグで Object.prototype.proto を無効化する
・Object.hasOwn(obj, key) で「そのオブジェクト直属か」を確かめる
・Object.prototype 由来のメソッドを使わない
いちばん効くのは「存在するか」の判定を変えること
実務で最も広く効くのは Object.hasOwn(obj, key) への置き換えです。単なる存在チェック(プロパティが「ある」かどうか)は、ひな型から継承した値も「ある」と答えます。つまり注入された値をそのまま拾う。「直属のプロパティか」まで確かめるだけで、①の被害経路の多くが閉じます。
同じ理由で、obj.hasOwnProperty(key) をオブジェクト自身から呼ぶ書き方も避けます——そのメソッド自体が汚染で消え得るからです(②の症状)。
自分のコードで確認すること
外部入力を「深いオブジェクトのまま」受け取っていないか
リクエストボディ、クエリ文字列、フォーム、Webhookのペイロード——深い階層を丸ごと受け取って既存の設定に合成している箇所を洗い出します。ここが唯一の入口です。スキーマで受け取るキーを明示的に決めるのが最短の解決です。
辞書として使うオブジェクトの作り方を変える
「キーが動的に決まる入れ物」(IDをキーにしたマップ、設定の集合など)は、Object.create(null) で作る。ひな型が無ければ、そもそも継承される値がありません。構造で解決できるものを、検証で解決しようとしないのが原則です。
存在チェックを Object.hasOwn に統一する
プロパティの有無で分岐している箇所を検索して置き換えます。とくに権限・フラグ・オプションの判定は優先度が高い。ここは認可の実装と直結します。
依存を機械で見張る
自分が再帰的マージを書いていなくても、依存が書いていれば同じ結果になります。公式が挙げた実例にサードパーティライブラリが含まれているのはそのためです。依存の既知脆弱性は人力では追えないので、osv-scannerの導入と使い方 のように機械監視に載せてください。汚染された依存が持ち込むリスク全般は npmのサプライチェーン汚染にどう備えるか にまとめています。
サーバー側フレームワークの入口も同じ目で見る
Node製のフレームワークはリクエストのボディをデシリアライズする経路を必ず持っています。当サイトがNVDのデータで確認したところ、Next.jsの脆弱性はキャッシュ・ミドルウェア・Server Actions といったサーバー側の責務に集中していました(Next.jsとReactは脆弱性が多いのか)。「フロントのライブラリだから関係ない」という前提を捨てるところから始めてください。
当サイトの視点:責任の境界が宣言されている場所こそ、点検する価値がある
この記事でいちばん実務的な情報は、緩和策の一覧ではなく「Node.jsコアの脆弱性とは見なさない」という一文だと考えています。境界線がはっきり引かれている場所は、その外側に誰もいない——CVEも出ないし、アップデートでも直りません。
当サイトはこれを、証明書が乗っ取りを守らないこと(サブドメイン乗っ取り)や、公開ブロックが既存のポリシーを消さないこと(クラウドストレージの公開設定ミス)と同じ型の問題として扱っています。守られていると思っていた場所の、実際の担当者は誰か。 それを一度確かめるだけで、対策の優先順位はかなり変わります。
出典(一次情報)
- Node.js「Security Best Practices」(Prototype Pollution Attacks / CWE-1321 の節) — nodejs.org(脅威モデル上の位置づけ、注入とDoSという2つの結果、実例のCVE、7つの緩和策は、いずれもこの文書の記述にもとづきます。2026-09-05に確認)
次に読む
- 依存:osv-scannerの導入と使い方 / npmのサプライチェーン汚染にどう備えるか
- 傾向:Next.jsとReactは脆弱性が多いのか(サーバー側に何が出ているか)
- 設計:認証と認可の違い(注入された値を認可の判定に使わないために)
よくある質問
Qプロトタイプ汚染は Node.js のバグですか? アップデートを待てば直りますか?
待っても直りません。Node.js公式のセキュリティ指針は『Node.jsの脅威モデルでは、攻撃者が利用者入力を制御することに依存するプロトタイプ汚染は、Node.jsコアの脆弱性とは見なされない。Node.jsはアプリケーションコードが与える入力を信頼するためである』と明記しています。そのうえで『とはいえプロトタイプ汚染はNode.jsアプリケーションおよびサードパーティライブラリにとって深刻な脆弱性のクラスであり、アプリケーションと依存関係の層で防御を実装すべきである』と続きます。つまり、これは仕様上あなたの責任範囲です。
Q具体的に何が起きるのですか?
2種類の結果があります。ひとつは、設定した覚えのないプロパティが至るところのオブジェクトに現れること。権限フラグやオプションの判定がその値を読んでしまうと、認可やビジネスロジックが狂います。もうひとつはサービス停止で、組み込みプロトタイプを壊すと `hasOwnProperty` のような標準メソッドが『関数ではない』というエラーになり、無関係な場所でアプリが落ちます。後者は『権限昇格だけの話』という思い込みを外してくれる、分かりやすい実害です。
Qどこを直せばいいですか?
入口とデータ構造の両方です。入口では、外部からの信頼できないリクエストにJSONスキーマ検証を掛け、想定外のキーを最初から通さない。データ構造では、安全でない再帰的マージをやめ、辞書として使うオブジェクトは `Object.create(null)` でプロトタイプを持たずに作る。読むときは `Object.hasOwn(obj, key)` でオブジェクト直属かを確かめ、`Object.prototype` 由来のメソッドに頼らない。運用面では `Object.freeze` と、Node.js の `--disable-proto` フラグがあります。
Q自分のコードで再帰的マージを書いていなければ安全ですか?
いいえ。実例として公式が挙げているのは Node.js 本体(CVE-2022-21824)とサードパーティライブラリ(Lodash の CVE-2018-3721)の両方です。設定のマージ、クエリ文字列のパース、フォームのデシリアライズなど、深いオブジェクトを組み立てるライブラリは広く使われています。だから対策は自分のコードだけでは閉じず、依存の機械監視とセットになります。