ページ)ここからは、MongoDB以外のNoSQLを使うアプリケーションのセキュリティについて簡単に説明します。2回に分けて、NoSQLのうち「MongoDB」を用いたWebアプリケーションの脆弱性と対策について紹介してきました。ここからは、MongoDB以外のNoSQLを使うアプリケーションのセキュリティについて簡単に説明します。取り上げるのは、Cassandra、Redis、memcachedの3つのNoSQLデータベースです。Cassandraについて見ていきます。Cassandraを使うアプリケーションのセキュリティCassandraは列指向型に分類されるNoSQLデータベースで、大容量のデータを高速に処理することに主眼を置いた製品です。元々はFacebookで開発されたデータベースですが、現在はApacheプロジェクトの一部としてオープンソースになっています。執筆時点の最新バージョンはv1.2.1で、本記事の内容はその挙動に基づきます。Language)というクエリ言語をサポートするようになりました。CQLはSQLと同等の構文を持つ言語であり、CQLを使うアプリケーションに不備があるならば、SQLインジェクションと似たような攻撃が可能です。脆弱なアプリケーション下記は、JavaとCassandra、CQLを使ってセッション情報を管理するプログラムの例です。この例では、JavaとCassandraの連携にJDBC(cassandra-jdbc)を使用しているため、SQLを使うデータベースアプリケーションと同様のスタイルのプログラムになっています。....このプログラムは、セッションIDを入力パラメータとして受け取り、それをキーにセッションテーブル(sessions)を検索します。セッションテーブルに該当行が存在する場合には、ログイン済みユーザーとみなします。このプログラムに対する正常なパラメータ例を下記に示します。sessionId=sess_b4c28bf0c8da17f88ff6a4ea1b91285a攻撃例SQLインジェクションに関する知識がある方ならばお分かりだと思いますが、このプログラムには、文字列連結でCQL文を作成しているという問題があります。この問題を突く攻撃の例を下記に示します。攻撃の目標は、正しいセッションIDをサーバに送ることなく、別の会員になりすますことです。user_id='victimこの操作により実行されるCQL文は下記となります。user_id='victim'cassandra-jdbc(バージョン1.2.1)で検証したところ、「key=''」が条件として無視されてしまい(※注1)、「user_id='victim'」の条件のみに合致する行が取得されました(ここでは、user_idにインデックスが設定されていると仮定しています)。なると、セッションIDを推測するまでもなく、user_idの推測のみでなりすましができることになります。なりすまし以外の被害としては、データベース内のデータの窃取が思い付くところですが、これは一筋縄ではいきません。関数・演算子がほとんど存在しない(OR演算子も使用できない)インデックスがないデータは検索できない一度に実行できるのは1つのCQL文のみに限られるなどの機能上の強い制限が存在するからです。SQLインジェクション攻撃においては、SQLに備わるさまざまな機能を駆使してデータを窃取しますが、少なくとも現行のCQLの機能は強く制限されているため、CQLインジェクションを悪用してデータを窃取するなどの攻撃が可能なケースは限られます。通常のSQLインジェクションと同じく、プレースホルダの使用が対策となります。JDBCを使用している際の具体的なプログラム例は下記の通りです。stmt.executeQuery();注1:WHERE句内の「key=''」が無視される現象は、phpcassa(PHPプログラムからCassandraにアクセスするためのライブラリ。バージョン1.0.a.6)を使ったPHPプログラムでは発生しませんでした。phpcassaでは空文字列のキーはエラーを発生させます。phpcassaにおいても、状況によってはSELECT文のWHERE条件の一部が評価されない現象が発生しました。マニュアルなどに詳細な情報がないため、バグかどうかは不明ですが、現状のCQLは動作の一貫性が不十分であるように思えます。Redisを使うアプリケーションのセキュリティRedisは、Key-Value型に分類されるオープンソースのNoSQLデータベースです。リスト型/集合型など多くのデータ型を扱えること、揮発性のKey-Value型システムとは異なりデータの永続化(ディスクへの保存)が可能なことなどが特徴です。執筆時点の最新バージョンは2.6.10で、本記事の内容はその挙動に基づきます。Redisはバージョン2.6.0からLuaスクリプトをサポートしています。Injection)に似たインジェクション攻撃のリスクがあります。下記は、Luaスクリプトを使った関数(コマンド)を定義しているPHPのプログラムの一部です。登録したコマンドを実行する$client->leftpad('abc');このプログラムを実行すると、Redisデータベースにleftpadというコマンド(関数)が作成されます。プログラム内の「<<<LUASCRIPT」と「LUASCRIPT;」の間の行がLuaスクリプトです。通常、Luaスクリプトにはデータベースの参照/更新処理などを実施させますが、上記プログラムではそういった処理は省いています。ポイントは、Luaスクリプト内に、入力パラメータ「pad_length」の値を埋め込んでいる部分です。正常なパラメータの値は下記のような数値です。pad_length=20攻撃例Luaスクリプト内に入力パラメータの値をそのまま埋め込んでいるため、パラメータを操作することで、攻撃者は任意のLuaスクリプトをRedisサーバ上で実行させることができます。さまざまな攻撃が可能ですが、まずはDoS(サービス利用妨害)攻撃の例を3つ示します。redis.call("FLUSHALL")(A)のパラメータは無限ループを利用しています。RedisのLuaスクリプトは、MongoDBのJavaScriptと同じくシングルスレッドで実行されるため、他のリクエストも含めてブロックされてしまいます。(B)はRedisのSHUTDOWNコマンドを実行してRedisサーバプロセスを停止させています。(C)はRedisのデータをすべて削除します。データベース内のデータを窃取・改ざんする攻撃も可能です。下記はデータを窃取する攻撃の例です。SQLインジェクションの攻撃手法にブラインドSQLインジェクションというものがありますが、攻撃の原理はそれと同じです。endredis.call("GET","foo")によってキー「foo」の値を取得し、その1文字目をsub(substring)によって切り出しています。「foo」の値の1文字目が「a」の場合は1をreturnし、そうでなければ存在しないコマンド「x」が実行され、内部エラーが発生します。HTTP応答の差から、1がreturnされたのか、それとも内部エラーが発生したのかが推測できるならば、上記の操作パラメータによって、値の1文字目に関する情報(「a」なのか、そうでないのか)が得られます。同様のリクエストを多く送ることで、値を特定できます。詳細は割愛しますが、応答にRedisのエラーメッセージが出力される状況であれば、より簡単に情報を窃取することもできます。MongoDBと同様に、RedisでもOSコマンドの実行やファイルへのアクセスを行う関数などは使用できないようにされています。dofile関数を通じて限定的ながらファイルシステムにアクセスできる問題が知られています。Securityhttp://blog.liftsecurity.io/post/35714931664/filesystem-enumeration-using-redis-and-lua対策Luaスクリプト文字列に値を埋め込まないようにします。具体的な方法はいくつか考えられますが、引数の形でLuaスクリプトに対して値を渡す仕組みがあるため、それを利用するのがよいでしょう。先に挙げた脆弱なアプリケーションの例においても、1つの引数(str変数)をPHP側から受け渡しており、他の変数(pad_length)もそれに倣えばよいということになります。Luaスクリプト側では、ARGVまたはKEYSから引数を取得することができます。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。