クエリに $_GET パラメータを渡している場合は、 まず最初にそれを文字列にキャストすることを忘れないようにしましょう。 GET リクエストには連想配列を入れることもでき、 そのまま使うと期待通りのクエリにはなりません。
当たり障りのない例を示します。ユーザーの情報を調べるときに http://www.example.com?username=bob のようなリクエストを送っているとしましょう。アプリケーション側では $collection->find(array("username" => $_GET['username'])) のような問い合わせをします。
ここで、誰かが http://www.example.com?username[$ne]=foo のようなリクエストを送ったとします。PHP はこれを自動的に連想配列に変換するので、クエリは $collection->find(array("username" => array('$ne' => "foo"))) のようになります。これは、名前が "foo" ではないユーザーすべて (おそらく全員でしょうね) の情報を返すことになります。
この攻撃を防ぐのはきわめて簡単です。$_GET パラメータが期待通りの型であることを確かめてから データベースにリクエストを送ればよいのです (この場合は、文字列にキャストすることになります)。
この種の攻撃は、ドキュメントを指すすべてのデータベース操作で可能であることに気をつけましょう。 更新や upsert、find した内容の修正、削除などがこれにあたります。
» Phil の指摘に感謝します。
» メインドキュメント には、MongoDB における SQL インジェクション風の問題に関する詳細な情報があります。
JavaScript を使う場合、PHP と JavaScript をまたぐ変数の受け渡しは MongoCode の scope フィールドで行いましょう。JavaScript の文字列にしてはいけません。 このような受け渡しが発生するのは、MongoDB::execute() や $where 句、MapReduce、group-by、そしてその他 JavaScript をデータベースに渡す場合です。
注意:
MapReduce は MongoCode の scope フィールドを無視しますが、コマンドのオプション scope をかわりに使うことができます。
たとえば、JavaScript を使ってデータベースログのユーザーにあいさつをするとしましょう。 こんなふうに書くことができます。
<?php
// これはやっちゃだめ!
$username = $_POST['username'];
$db->execute("print('Hello, $username!');");
?>
しかし、悪意のあるユーザーがこんな JavaScript を渡したとしたらどうなるでしょう?
<?php
// これはやっちゃだめ!
// $username に "'); db.users.drop(); print('" が入っているとすると…
$db->execute("print('Hello, $username!');");
?>
このとき MongoDB が実行する JavaScript 文字列は "print('Hello, '); db.users.drop(); print('!');" となります。この手の攻撃を回避するのは簡単で、変数を PHP から JavaScript に渡すときに scope を使います。
<?php
$scope = array("user" => $username);
$db->execute(new MongoCode("print('Hello, '+user+'!');", $scope));
?>
これは、変数 user を JavaScript のスコープに渡します。 もし誰かが悪意のあるコードを仕込んだとしても、MongoDB は何の被害も受けずに単に Hello, '); db.dropDatabase(); print('! と表示するまでです。
scope を使えば、悪意のある入力がデータベースで実行されることを防ぐ助けになります。 しかし、コードの中で入力を実行してしまわないように注意が必要です。 たとえば、JavaScript eval 関数をユーザーの入力に対して決して使ってはいけません。
<?php // これはやっちゃだめ! // $jsShellInput は "db.users.drop();" です $scope = array("input" => $jsShellInput); $db->execute(new MongoCode("eval(input);", $scope)); ?>
常に scope を使い、 決してユーザーの入力をデータベースのコードとして実行させないようにしましょう。