NoSQLインジェクションの深淵:MongoDBの演算子汚染が暴く「型」の脆弱性
エンジニア諸君、セキュリティの現場において「SQLインジェクションは過去の遺物」という慢心は、即座に死を意味する。今日、我々が直面しているのは、リレーショナルデータベースの構造的な制約を飛び越え、JSONライクなドキュメント志向データベースの柔軟性を逆手に取った「NoSQLインジェクション」だ。
特にMongoDBを標的とした認証バイパスは、プロトコルの仕様を正しく理解していない開発者の「型への無頓着さ」を鋭く突いてくる。今日は、$gt や $ne といったクエリ演算子が悪用されるメカニズムと、それを防ぐためのアーキテクチャ設計について、一段深い層から解説しよう。
—
1. 攻撃の解剖:なぜ {"$gt": ""} で認証が突破されるのか
MongoDBのクエリエンジンは、クライアントから送られてくるJSONオブジェクトをクエリドキュメントとして解釈する。ここで致命的なのが、「入力値として文字列が期待されている場所に、演算子を含むオブジェクトが注入された場合」の挙動だ。
例えば、ユーザーのログイン処理で以下のようなコードがあったとしよう。
// 脆弱な実装例
const query = {
username: req.body.username, // ここに {“$gt”: “”} が注入される
password: req.body.password
};
const user = await db.collection(‘users’).findOne(query);
攻撃者が username に {"$gt": ""} というJSON文字列を送り込んだ場合、MongoDBはこれを「usernameが空文字より大きいドキュメントを探せ」と解釈する。結果、全ユーザーの中で辞書順で空文字よりも後ろにくる最初のユーザーが抽出され、多くの場合、最初の管理者が意図せず認証されてしまう。
これはSQLインジェクションで言うところの ' OR '1'='1 に相当するが、より巧妙なのは「型をすり替えている」点にある。アプリケーション層が req.body.username を文字列として想定しているにもかかわらず、データベースドライバが意図せずクエリ演算子として再帰的にパースしてしまう。これがこの脆弱性の根本原因だ。
—
2. メモリとパケットの境界:プロトコル仕様の盲点
MongoDBのバイナリプロトコル(OP_QUERYやOP_MSG)は、BSON形式でシリアライズされる。攻撃者は、HTTPリクエストのContent-Typeを application/json に偽装し、ボディにオブジェクトを埋め込むだけで、ドライバ側で自動的にBSONドキュメントに変換させることができる。
ここで重要になるのは、「入力をどこで止めるか(Sanitize)」ではなく「入力をどう定義するか(Type Validation)」という設計思想だ。
防御戦略:型定義の強制(Schema Validation)
Node.js環境であれば、Joi や Zod を使った入出力のバリデーションは必須だ。しかし、アーキテクトとしてはそれだけでは足りない。データベースレベルでのスキーマ検証を実装せよ。
// MongoDBのコレクション作成時にスキーマバリデーションを適用する
db.createCollection(“users”, {
validator: {
$jsonSchema: {
bsonType: “object”,
required: [“username”, “password”],
properties: {
username: {
bsonType: “string”, // 型を強制することで演算子オブジェクトの注入を拒否する
description: “username must be a string”
}
}
}
}
});
この設定を行えば、仮にアプリケーション層でバリデーションをすり抜けたとしても、データベース側で不正な型(オブジェクト)の挿入やクエリが拒絶される。これが「多層防御」の極致だ。
—
3. 生成AI時代のガードレイルと脅威モデル
最近では、LLMを介したプロンプトインジェクションにより、MongoDBへのクエリ生成が自動化されるケースも想定される。AIが生成したコードが、意図せずNoSQLインジェクションを誘発するようなクエリを構築するリスクだ。
これに対する我々のアーキテクチャは以下の通りだ。
- 入力の正規化(Normalization): APIゲートウェイ層で、すべてのリクエスト値を明示的にプリミティブ型(string, number)にキャストする。
- クエリビルダの制限: ODM(Mongooseなど)の利用時、ユーザー入力を直接クエリ条件に含めず、一度ホワイトリストで構成されたフィルタリング関数を通す。
- 特権分離: MongoDBのユーザー権限を最小化し、特定のコレクションに対しては
find権限のみを付与し、演算子を含むクエリの実行を監視・拒否するプロキシレイヤを挟む。
—
結論:プロのセキュリティとは「境界」を管理すること
NoSQLインジェクションの脅威の本質は、開発者が「データベースへのクエリは安全な文字列として送られる」と思い込んでいる、その甘い信頼境界にある。
真のセキュリティスペシャリストは、データがアプリケーションの境界を越えるたびに、それが期待される型であるかを疑い、物理的なバリデーションを実装する。技術が複雑化し、NoSQLの柔軟性が増せば増すほど、我々はより原始的で厳格な「型チェック」という防御壁へ立ち返らねばならない。
次のコードをレビューする際、その変数が「文字列」であると仮定している箇所が一つでもあれば、そこにNoSQLインジェクションの隙がある。そう自覚せよ。コードを信じるな、定義を信じろ。それが、最前線で戦う我々のルールだ。
コメント