【入門編】NoSQLインジェクション:クエリ演算子($gt, $ne等)による認証バイパス – アプリケーションセキュリティ & 安全な開発防御ガイド

玄関の鍵を「魔法の言葉」で開けられてしまう?NoSQLインジェクションの脅威と対策

こんにちは。セキュリティの現場で日々、防御の最前線に立っているエンジニアです。

今日は、開発者なら一度は耳にする「インジェクション攻撃」の中でも、特にモダンなアプリで狙われやすい「NoSQLインジェクション」についてお話しします。

「NoSQLってSQLじゃないから安全なんでしょ?」なんて油断していませんか?実は、その思い込みこそが一番の狙い目なんです。専門用語を並べるのは一旦置いておいて、まずは身近な例えから紐解いていきましょう。

—

1. 「合鍵」が「魔法の杖」に変わる瞬間

想像してみてください。あなたは自分の家の玄関に、「暗証番号を入力するタイプの電子錠」を設置しました。正しい暗証番号を打てばドアが開く、ごく普通の仕組みですよね。

通常、プログラムはこんな風に考えています。
「入力された暗証番号」は「データベースにある正しい番号」と一致するか?

  • 正しい暗証番号(1234) → 「一致する(True)」→ ドアが開く
  • 間違った番号(9999) → 「一致しない(False)」→ ドアが開かない

これが正常な状態です。しかし、攻撃者はここに「意味をすり替える魔法の言葉」を混ぜ込みます。

攻撃のメカニズム:$gt というズルい演算子

NoSQLデータベース(MongoDBなど)では、JSON形式で条件を送ります。攻撃者は暗証番号を入力する場所に、普通の数字ではなく、「$gt(Greater Than:〜より大きい)」という命令を送り込むのです。

もし攻撃者が「0より大きい($gt: 0)」という命令を送ったら、システムはどう判断するでしょうか?
「入力された数字は0より大きいか?」→「はい、入力値(の形式)は正しいので、データベースにある全てのデータが条件に該当する(True)」と勘違いしてしまいます。

結果として、「一番上にある管理者アカウント」の認証を勝手にパスしてログインできてしまう……これがNoSQLインジェクションの正体です。

—

2. コードで見る「やらかし」と「修正」

では、実際にJavaScript(Node.jsなど)で書かれたサーバーサイドのコードを見てみましょう。

悪い例:入力をそのまま受け取ってしまう

// 危険なコード:ユーザーの入力をそのまま検索条件にしてしまっています
app.post(‘/login’, async (req, res) => {
const query = {
username: req.body.username,
password: req.body.password // ここに {“$gt”: “”} と送られるとアウト!
};

const user = await db.collection(‘users’).findOne(query);
if (user) {
res.send(“ログイン成功!”);
}
});

攻撃者が password の欄に {"$gt": ""} と書き込んで送ると、データベースは「パスワードが空文字より大きいユーザーを一人見つけてきて」と解釈し、最初のユーザーでログインさせてしまうのです。

—

3. 一歩ずつ対策を学んでいきましょう!

この攻撃を防ぐための鉄則は、「ユーザーからの入力を信用しないこと」です。家の鍵に例えるなら、「どんな言葉を言われても、絶対に開けてはいけない相手にはドアを開けない」というルール作りです。

対策その1:型をしっかりとチェックする

入力された値が「文字列」であることを強制しましょう。演算子($gtなど)はオブジェクト型なので、文字列として処理するように制限すれば、悪意ある命令はただの「文字列」として扱われ、機能しなくなります。

// 安全なコード:型を文字列に固定する
app.post(‘/login’, async (req, res) => {
const username = String(req.body.username); // 明示的に文字列へ変換
const password = String(req.body.password); // これでオブジェクトは渡せなくなる

const user = await db.collection(‘users’).findOne({
username: username,
password: password
});
// …
});

対策その2:バリデーションライブラリを使う

Joi や express-validator といったバリデーションライブラリを使うのも非常に効果的です。「入力は英数字のみ」「最大20文字」といった厳しい門番を立てることで、攻撃者が入り込む隙間を物理的に消してしまいます。

—

最後に:セキュリティは「泥臭い積み重ね」です

「NoSQLだから大丈夫」という神話は、今日で卒業しましょう。攻撃者は、私たちが「まさかこんな書き方はしないだろう」と思っている盲点を、驚くほど冷静に突いてきます。

1. 入力値は必ず型を確認する
2. ライブラリを信頼しすぎず、自分でフィルタリングする
3. 「もしこれが悪意ある命令だったら?」と常に疑う

この3つを意識するだけで、あなたの書くコードの堅牢性は劇的に向上します。最初は面倒に感じるかもしれませんが、それは「頑丈な鍵」を一つ増やす作業と同じです。

明日からの開発で、ぜひ「この入力は、本当にただのテキストかな?」と自分に問いかけてみてください。その小さな疑念こそが、あなたのサービスとユーザーを守る最強の盾になります。

それでは、安全なコーディングライフを!

コメント

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