LoginSignup
1
0

MongoDBにおけるNoSQLインジェクション: 脆弱性の仕組みと対策

Last updated at Posted at 2023-11-02

データベースを使ったシステムのセキュリティで、特に気をつけなければならないものの一つが、SQLインジェクションです。SQLインジェクションとは、外部からの意図しない入力により不正なSQL文を実行されてしまう脆弱性です。SQLで操作するデータベースにおいて、ユーザーからの入力を適切に検証・エスケープせずにSQLクエリに埋め込むような実装になっていた場合に発生します。

NoSQLデータベースを使用すればSQLインジェクションは発生しませんが、その場合NoSQLインジェクションに注意する必要があります。

この記事ではMongoDBを使ったシステムの実装の例を元に、NoSQLインジェクションを解説していきます。

NoSQLとSQLの違い

SQLとはStructured Query Languageの略で、リレーショナルデータベースのデータを操作・取得するための標準的なプログラミング言語です。

それに対してNoSQLとは、リレーショナルデータベースとは異なるデータモデルを採用したデータベースの総称を言います。MongoDBやRedisのようなデータベースがNoSQLに分類されます。

SQLはプログラミング言語なのに対し、NoSQLはSQLを使わない非リレーショナルデータベースのことを言います。

NoSQLインジェクションとは?

NoSQLインジェクションとは、NoSQLデータベースで発生するインジェクションの脆弱性です。NoSQLではデータの操作にSQL文を使わずに、独自のクエリ言語、独自のAPIを使用します。データベースの種類ごとに違う方法で操作するため、インジェクションの発生パターンも一律ではありません。

とはいえ、いくつか共通の対策もあり、

  • パラメータ化されたクエリを使用する
  • リクエストパラメータが不正な形式だった場合は処理を行わない

などを行うことで、リスクを低減させることが可能です。

脆弱な実装の例

使用する環境

  • プログラミング言語
    • Node.js
  • データベース
    • MongoDB
  • ライブラリ
    • mongoose
    • express

簡易なWebサービスのログイン用APIを例にとって解説していきます。データベース操作はMongoDB操作用のライブラリ「mongoose」を使用します。

Userモデル

username: String
password: String
エンドポイント: POST /login
送信するJSON
{
  username: 'john',
  password: 'hoge',
}

ケース1: 評価式の書き換え

app.js
app.post('/login', async (req, res) => {
  const username = req.body.username;
  const password = req.body.password;
  const users = await User.find({
    $where: `this.username === '${username}' && this.password === '${password}'`
  });
  if (users.length === 0) {
    res.json({
      message: 'Failed to log in.'
    });
    return;
  }
  res.json({
    message: `Successfully logged in as ${users[0].username}`
  });
});
$where: `this.username === '${username}' && this.password === '${password}'`

このような実装の場合、通常下記のように実行されます。

$where: `this.username === 'john' && this.password === 'hoge'`

このusernameやpasswordにシングルクォート文字列が含まれていた場合、新たな条件式を追加できます。

$where: `this.username === 'john' && this.password === '' || this.username === 'admin'`

このような文字列を挿入すると、パスワードなしで他のユーザーとしてログインができてしまいます。

対策方法としては、$whereの使用をやめて、下記のようなパラメータ化されたクエリに置き換えることが挙げられます。

const users = await User.find({
  username: username,
  password: password,
})

ケース2: オペレータ追加

app.js
app.post('/login', async (req, res) => {
  const username = req.body.username;
  const password = req.body.password;
  const users = await User.find({
    username: username,
    password: password,
  });
  if (users.length === 0) {
    res.json({
      message: 'Failed to log in.'
    });
    return;
  }
  res.json({
    message: `Successfully logged in as ${users[0].username}`
  });
});

このような実装になっている場合、通常下記のように実行されます。

const users = await User.find({
  username: 'john',
  password: 'hoge',
});

このusernameやpasswordが文字列形ではなく、オブジェクト形式として受け取っていた場合、

const users = await User.find({
  username: 'john',
  password: {
    $ne: 'dummy'
  },
});

$neはMongoDB用の比較用のオペレータです。Not Equal(一致しない)の意味となるため、パスワードが一致しなくてもログインが成立してしまいます。

対策としては下記のような修正が有効です。

  • パラメータの型がオブジェクト型ではないことを確認する
  • パラメータの型が適切な型である(例では文字列型)ことを確認する

まとめ

MongoDBなどのNoSQLデータベースを使用する場合、そのデータベースのクエリ言語やAPIでインジェクションが起こる可能性がある箇所を洗い出して、適切に入力チェックをする必要があります。

MongoDBの場合、具体的には下記の方法で防ぐことが出来ます。

  • パラメータ化されたクエリの使用($whereを使用しない)
  • パラメータの型のバリデーション

おわりに

AeyeScanはSaaS型のWebアプリケーション脆弱性診断プラットフォームです。かんたんに高精度なWeb診断を実施することができることから、Webアプリ診断の内製化ツールとして多くの企業様にご活用いただいております。

今回解説したMongoDBのNoSQLインジェクションも検査することができますので、ご興味ある方はぜひトライアルにてお試しください。
AeyeScanの詳細はこちら: https://www.aeyescan.jp

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0