MongoDBを用いたWebアプリケーションで生じる可能性がある4種類の脆弱性のうち、今回は「JSON文字列へのインジェクション」と「パラメータの追加」のメカニズムと対策について説明します。
前回の「『演算子のインジェクション』と『SSJI』」では、MongoDBを用いたWebアプリケーションで生じうる脆弱性のうち「演算子のインジェクション」と「SSJI」について、攻撃の実例と対策について解説しました。今回はさらに、「JSON文字列へのインジェクション」と「パラメータの追加」について説明します。
これまで見てきたように、PHP言語においては連想配列を指定してデータの登録処理や検索処理を実行できます。しかし型の扱いが厳格な言語では、もう少し手間の掛かるプログラミングが必要になることがあります。ここではそのような例として、Javaで書かれたデータ登録プログラムを取り上げ、その脆弱性について説明します。
脆弱なアプリケーションの説明をする前に、Java言語においてMongoDBにデータを登録する一般的なプログラム(脆弱ではないプログラム)を紹介します。対比のために、同じ処理を行うPHPとJavaのプログラムを記述しています。
// PHP版 // データを登録 $member = array('type' => 'member', 'name' => 'MBSD', 'interests' => array('music', 'travel'); $db->members->insert($member);
// Java版 // interests配列を作成 ArrayList<String> interests = new ArrayList<String>(); interests.add("music"); interests.add("travel"); // 登録するデータの全体を作成 BasicDBObject member = new BasicDBObject("type", "member") .append("name", "MBSD") .append("interests", interests); // データを登録 db.getCollection("members").insert(member);
前者のPHPのプログラムと比べると、後者のJavaのプログラムがデータ構造に依存した煩雑な処理になっていることは否めません。そのためだと思われますが、ネット上には下記のように、JSON文字列を生成してクエリを行うJavaのプログラムも紹介されています。
// JSON文字列をDBObjectに変換する String json = "{'type':'member', 'name':'MBSD', 'interests':['music','travel']}"; DBObject member = (DBObject) JSON.parse(json); // データを登録 db.getCollection("members").insert(member);
上記プログラムのJSONクラスは、MongoDBに含まれているクラス(com.mongodb.util.JSON)です。このクラスは文字列の括り文字として「'」も受け入れます。説明を簡単にするため、以降のプログラムでは「'」を使用します。
JSON文字列が固定ならば、上記のようなプログラムに問題はありません。しかし、下記のプログラムのように、入力パラメータから取得した値をそのまま埋め込んでJSONを生成してしまうと、その限りではなくなります。
// リクエストパラメータから各要素の値を取得 String name = request.getParameter("name"); String interest1 = request.getParameter("interest1"); String interest2 = request.getParameter("interest2"); // JSON文字列を生成し、DBObjectに変換する String json = "{'type':'member', 'name':'%1$s', 'interests':['%2$s','%3$s']}"; json = String.format(json, name, interest1, interest2); DBObject member = (DBObject) JSON.parse(json); // データを登録 db.getCollection("members").insert(member);
上記プログラムの正常なパラメータ例を下記に示します。
正常パラメータ例: name=MBSD&interest1=music&interest2=travel
JSON文字列内でtype(会員種別)は、'member'の固定値となっていますが、これを回避してtypeが'admin'のユーザーを登録することが攻撃の目標です。
攻撃者はJSON文字列を生成している個所を突いて攻撃を行います。具体的には、下記のように操作したパラメータを送信します。
操作パラメータ例: name=MBSD', 'type':'admin&interest1=music&interest2=travel
すると、プログラムにより生成されるJSON文字列は下記となります。
{'type':'member', 'name':'MBSD', 'type':'admin', 'interests':['music','travel']}
JSON文字列には、ハードコードされている「先頭のtype」と攻撃者が挿入した「後方のtype」の2つが存在しますが、実際にMongoDBに登録される値は下記のようになります。データベースには、後方の値によってtypeが'admin'に上書きされたデータが登録されてしまったことが分かります。
{ "_id" : ObjectId("512dd4fae4b0f17388cb3983"), "type" : "admin", "name" : "MBSD", "interests" : [ "music", "travel" ] }
対策の選択肢は2つあります。1つは、先に挙げた脆弱性のないJava版プログラムのように、JSON文字列を経由しない方法でクエリを生成する方法です。もう1つは、JSON文字列を生成する際に、文字列についてはエスケープ、数値/真偽値については型変換を行う方法です。文字列のエスケープについては、RFC4627に規定された処理を自作するか、既存のJSON処理用のJavaのライブラリを使用します。
Copyright © ITmedia, Inc. All Rights Reserved.