【テクニカル・上級編】NoSQLインジェクション(MongoDB等)の攻撃パターン – アプリケーションセキュリティ & 安全な開発防御ガイド

境界線の消失:NoSQLインジェクションが暴く「型安全」の幻想

多くのエンジニアがSQLインジェクション(SQLi)を過去の遺物だと高を括っている間に、我々が守るべきデータ層は「JSON」という名の柔軟な言語に侵食されている。MongoDBをはじめとするNoSQLデータベースは、スキーマレスという自由を手に入れた代償として、ロジックの境界線を曖昧にしてしまった。

今回は、単なる「入力チェックをしましょう」といった教科書的な話はしない。パケット構造の深淵から、アプリケーションの型システムがなぜNoSQLインジェクションを許容してしまうのか、そのメカニズムとアーキテクチャレベルの防衛論を紐解く。

—

1. 攻撃の解剖:演算子という名の「トロイの木馬」

NoSQLインジェクションの本質は、ユーザー入力を単なる「値」としてではなく、「演算子(Operator)」として解釈させてしまう点にある。

例えば、Node.js環境で以下のような認証ロジックを書いたことはないだろうか。

// 脆弱な認証クエリの例
const query = {
username: req.body.username,
password: req.body.password
};
const user = await db.collection(‘users’).findOne(query);

攻撃者が req.body.password に {"$gt": ""} というJSONを投げ込んだ瞬間、クエリは password: { $gt: "" } となる。これは「パスワードが空文字より大きい(=存在する)」という条件に書き換わり、認証が完封される。

なぜこれが防げないのか

この脆弱性の根源は、「入力の型(Type)が期待通りであると盲信していること」にある。プロトコル層ではJSONとしてパースされるが、データベースドライバ層に渡る際、MongoDBのクエリ演算子がそのまま「命令」として評価されてしまう。メモリ上でのオブジェクトのシリアライズ・デシリアライズの過程で、単なる文字列だったはずの入力が、構造化された制御命令へと昇華してしまうのだ。

—

2. 防御のアーキテクチャ:入力の「正規化」から「構造的強制」へ

一般的なバリデーション(if (!req.body.password) ...)では不十分だ。攻撃者は型を偽装する。我々が取るべきは、「入力の強制的な型変換」である。

推奨される防御実装(Mongoose/Joi等の活用)

入力値を、サーバー側で必ず「プリミティブな型」にキャストすること。

// 堅牢な実装の例
const schema = Joi.object({
username: Joi.string().alphanum().required(),
// パスワードは強制的に文字列として扱う。オブジェクトの混入を拒否する
password: Joi.string().required()
});

const { error, value } = schema.validate(req.body);
if (error) throw new Error(“Invalid input structure”);

// この時点でvalue.passwordは確実に文字列であることが保証されている
const user = await db.collection(‘users’).findOne({
username: value.username,
password: value.password
});

—

3. 生成AI時代の新たな脅威:プロンプトインジェクションとの交差点

現在、多くのアプリケーションがLLMとNoSQLを直結させている。ここで恐ろしいのは、ユーザーがLLMに対して入力した「自然言語による指示」が、結果としてNoSQLへのクエリとして生成されるケースだ。

LLMの出力が直接データベースドライバに渡るアーキテクチャは、極めて危険な「バックドア」を自ら開いているに等しい。

ガードレイルの設計指針

1. 中間層の分離: LLMが生成するクエリは、必ず一度「抽象構文木(AST)」として解析し、許可された演算子($eqのみ等)以外を物理的に排除するフィルタリング層を通すこと。
2. 特権分離: アプリケーションとデータベースの接続には、最小権限の原則を適用した専用のサービスアカウントを割り当てる。たとえインジェクションが成功しても、$where 演算子によるJavaScript実行や、コレクションの削除といった破壊的動作をOSレベルで遮断する。

—

4. 監査の観点:低レイヤからの検知

チーフホワイトハッカーとしてインフラを監査する際、私はアプリケーションログよりも「ドライバレベルのクエリ発行ログ」を注視する。

  • 異常なクエリ構造の検知: findOne に渡されるクエリオブジェクトの中に $gt, $ne, $regex などの演算子が含まれていないか。これらをSIEM(SplunkやDatadog等)でアラート監視する。
  • 通信プロトコル解析: MongoDBのWire Protocolをパケットキャプチャし、クエリのバイナリ構造を解析する。通常は期待されない複雑なネスト構造を持つクエリは、ほぼ間違いなく攻撃の兆候だ。

—

最後に:防御は「信頼の破壊」から始まる

NoSQLインジェクションを防ぐ唯一の道は、「外部からの入力は、いかなる形式(JSONであれバイナリであれ)であっても、プログラムの制御フローを乗っ取る意図を持つ可能性がある」と前提することだ。

クリーンなコードを書くことも重要だが、アーキテクトとしては「データがどこで解釈され、どこで命令になるのか」という、メモリと通信の境界線を冷徹に見極める必要がある。

型システムを信じるな。バリデーションを過信するな。データがプログラムに変換されるその瞬間に、全ての防壁を設置せよ。それこそが、この混沌としたサイバー空間で生き残る唯一の流儀だ。

コメント

タイトルとURLをコピーしました