NoSQLインジェクション:MongoDBに潜む「型」の罠と、設計思想から変える防御術
現場でコードレビューをしていると、未だに「NoSQLだからSQLインジェクションとは無縁」と本気で信じているエンジニアに出くわすことがある。はっきり言おう。それは致命的な誤解だ。
SQLが文字列結合の隙を突くのに対し、MongoDBのようなドキュメント指向データベースに対する攻撃――NoSQLインジェクションは、アプリケーションが受け取る「JSONオブジェクトの構造」そのものを悪用する。今回は、MongoDBを例に、なぜ彼らが認証をいとも簡単にバイパスできるのか、そしてそれを現場でどう根絶すべきか、実務的な視点で解説する。
—
1. なぜ「型」の混入が危険なのか(PoCのメカニズム)
MongoDBのクエリはJSON(実際にはBSON)で構築される。例えば、ログイン機能でユーザー名とパスワードを照合する際、バックエンドでは以下のような処理が行われがちだ。
// 脆弱な実装例(Node.js/Express)
db.collection(‘users’).findOne({
username: req.body.username,
password: req.body.password
});
正常な入力であれば、usernameとpasswordは文字列として扱われる。しかし、攻撃者はここに「オブジェクト」を送り込む。req.body.passwordに {"$ne": null} を仕込むのだ。
すると、生成されるクエリはこうなる。
{ "username": "admin", "password": { "$ne": null } }
$ne は「Not Equal(〜と等しくない)」を意味するクエリ演算子だ。つまり、「パスワードがnullではないユーザーを探せ」という命令にすり替わり、パスワードを知らなくてもログインが成立してしまう。これが認証バイパスの典型的なメカニズムだ。
—
2. 現場で今すぐ使える防御アプローチ
この脆弱性を防ぐためのアプローチは、大きく分けて「バリデーションによる型固定」と「クエリ構築の抽象化」の2点に集約される。
① 型の強制(Input Validation)
一番の基本は、ユーザー入力を「信頼できる型」に強制することだ。JSONとして送られてきたデータであっても、必ずサーバーサイドでデータ型をバリデーションする。
Node.jsでの実装例(Joiなどのバリデーターライブラリを使用)
const Joi = require(‘joi’);
// スキーマで型を厳密に定義する
const loginSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
// ここでpasswordを確実に「string」に制限する。オブジェクトの混入を許さない。
password: Joi.string().required()
});
// リクエストを検証
const { error, value } = loginSchema.validate(req.body);
if (error) {
return res.status(400).send(‘不正な入力形式です’);
}
// 検証済みの値のみを使用する
db.collection(‘users’).findOne({ username: value.username, password: value.password });
② ODM(Object Document Mapper)の活用
生のドライバを直接叩くのではなく、MongooseのようなODMを使うのが今の標準だ。ODMは内部的にスキーマ定義に基づいて型変換を行うため、意図しないクエリ演算子の混入を防ぐ安全装置として機能する。
—
3. セキュリティチーフとしての「推奨設定」
アプリケーションレベルの防御に加えて、インフラ層での多層防御も怠ってはならない。MongoDBの設定ファイル(mongod.conf)やネットワーク構成で以下のガードレールを引くことが重要だ。
MongoDBのセキュリティ設定(抜粋)
セキュリティ設定例
security:
# 認証を必ず有効化する(デフォルトだが稀に無効なケースがある)
authorization: enabled
net:
# 外部からの直接アクセスを遮断し、VPC内からの通信のみ許可する
bindIp: 127.0.0.1, <内部IPアドレス>
WAFでのリクエスト監視
WAF(AWS WAFなど)を利用している場合、JSON内に含まれるクエリ演算子($ne, $gt, $whereなど)をシグネチャとして検知するルールを追加する。
AWS WAF JSON Matchルール例:
- 検査対象:
JSON body - マッチ条件:
{"$ne":}や{"$gt":}を含むリクエストをBlockする。
—
最後に:エンジニアへのアドバイス
NoSQLインジェクションを防ぐために最も重要なのは、「入力をそのままクエリのパーツとして扱わない」というSQLインジェクション時代からの教訓を、JSONの世界でも徹底することだ。
「MongoDBだから大丈夫」という慢心は、脆弱性の最大の温床になる。コードを書くときは常に、「この値に配列やオブジェクトを突っ込まれたら、クエリの構造はどう変化するか?」を想像してほしい。
セキュリティは「魔法のツール」を導入して終わりではない。開発プロセスの中に、型チェックやバリデーションという「泥臭い習慣」を組み込めるか。それこそが、堅牢なシステムを作る唯一の道だ。
次のリリースでは、まずログイン画面と検索フォームのバリデーション定義を確認してほしい。もし「型」が放置されている場所があれば、そこが攻撃者の入り口だ。戦いは、コードの細部から始まっている。
コメント