パラメーター化クエリーを使用して DB2 Ruby on Rails アプリケーションのセキュリティーとパフォーマンスを強化する

DB2 のための新しい ActiveRecord アダプター

企業で Ruby on Rails を利用することへの関心が高まるなか、一部のオブザーバーは、企業による厳しい要件に対応する上での Ruby on Rails の適格性について問題を提起しています。そうした問題の 1 つは、Rails の ORM (Object-Relational Mapper) である ActiveRecord では、Prepared Statement を使用しない (少なくとも今まではそうでした) という点です。しかし、最新リリースの DB2 on Rails では、パラメーター化クエリーが自動的に使用できるようになっており、DB2 on Rails を利用することで Rails アプリケーションのパフォーマンスおよびセキュリティーに大きなメリットがもたらされるようになっています。

Antonio Cangiano, Software Engineer and Technical Evangelist, IBM

Antonio Cangianoの顔写真Antonio Cangianoは、IBM Toronto Software LabのDB2チームに勤務するソフトウェア・エンジニア兼テクニカル・エバンジェリストです。彼はDB2対応Railsアダプターを最初に開発した人物で、DB2 on Railsコミュニティーのリーダーの1人でもあります。Ruby and Railsコミュニティーで名の知られたCangianoは、2009年に著書「Wrox, Ruby on Rails for Microsoft Developers」を出版しました。彼のブログ、Zen and the Art of Programmingにアクセスして、彼が書いた他の記事も読んでください。



2010年 1月 21日

Ruby on Rails は、Web アプリケーションの開発に最もよく使用されているフレームワークの 1 つとしての地位を築き上げました。2004年にリリースされたこのフレームワークは急速に進化し、メディアの強い関心を引き付けるとともに、開発コミュニティーのなかで多くのファンを獲得しています。

ここ数年の間に、Rails (Ruby on Rails の通称) は少数の新しもの好きと新興企業だけが採用する最先端のツールから、成熟したツールへと変化しました。最近ではその生産性が、企業で Web アプリケーションの開発を行う人々を魅了するようになっています。

Rails と企業

Rails を話題にするときに、懐疑的な人々のなかから度々持ち上がってくる 1 つの疑問があります。それは、Rails が企業での利用に対応可能であるかどうかという疑問です。Rails が実利的で融通が利かないソフトウェアであることは間違いありません。企業を含め、万人を喜ばせるようには意図されていなかった Rails は、あまりにもプロセス主導で複雑すぎると受け取られることもあります。傾向として、企業のニーズと Rails 開発チームのニーズにはかなりの違いがあります。そのため、企業が必要とする機能を Rails 開発チームが提供するとは期待できません。

その一方で、企業のほうが Rails に対応可能かどうかという観点で、この件を見直す価値はあります。ここ数年間、さまざまな企業がアジャイル開発の動きを支持し始めています。さらに、より軽量なツールを支持するようにもなってきましたが、それでもまだ、Ruby on Rails が理想の選択肢とはならない確固たる要件が存在する状況が数多くあります。

ありがたいことに、Rails はアドオン・システムを提供しています。このアドオン・システムが、これまで何千ものプラグインの作成を可能にしてきました (これらのプラグインは、あらゆる類の要件を満たすためにコミュニティーによって開発されたものです)。けれども根本的に Rails が適用する原則は、企業が大抵の場合に優先する手法とは全く対照的に、「上手くいくかもしれない最も単純なこと」を行うというものです。

例えば、SQLite と MySQL はオープンソースの開発者の間では好評で、彼らの要求を十分満たしているかもしれませんが、企業の開発者にとって遥かに重要なのは、DB2 や Oracle などの商用データベースに対する堅実なサポートです。

かなり以前から、IBM ではオープンソースの世界と商用ソフトウェアの世界が 1 つに集約されるように取り組んできました。事実、IBM は、DB2 や IBM IDS (Informix® Dynamic Server) といった商用データベースに Ruby ドライバーと Rails アダプターを提供し、商業的なサポートをしている唯一のデータベース・ベンダーであり、DB2 をインフラとして使用する数多くの企業が、Ruby on Rails を使用して新しいアプリケーションを開発できるようにしてきました。いわゆる「DB2 on Rails」の組み合わせは、強力かつスケーラブルで信頼性に優れた (そして Express-C エディションでは無料の) データ・サーバーを、Ruby on Rails でのアプリケーション作成の単純さと組み合わせて利用したいと望んでいるオープンソースの開発者や新興企業にも最適です。

Rails アダプター・バージョン 2.0 の発表により、DB2 は現在、そのままの状態で (アプリケーションのコードを変更することもなく) Rails のパラメーター化クエリーをサポートする唯一のデータベースにもなっています。このことにはパフォーマンスとセキュリティーの両面において大きな意味があり、Rails を DB2 と併せて使用するすべての開発者にとって大きな前進となります。


パラメーター化クエリーと ActiveRecord

典型的な Web アプリケーションは、裏では多数の同じようなクエリーを何度も実行しているものです。場合によっては、クエリー自体は変わらずに、クエリーの中で使用する値が変わっているだけのこともあります。例えばリスト 1 に記載する複数のクエリーは、要求している名前を抜かせば、どれも全く同じです。

リスト 1. 同じような SQL クエリー
SELECT * FROM people WHERE name = 'Antonio'
SELECT * FROM people WHERE name = 'Leon'
SELECT * FROM people WHERE name = 'Praveen'
SELECT * FROM people WHERE name = 'Mario'

上記のすべてのクエリーは、リスト 2 に記載する 1 つのパラメーター化クエリー (Prepared Statement) としても知られています) に凝縮できます。

リスト 2. パラメーター化クエリー
SELECT * FROM people WHERE name = ?

実際の値は、事前にコンパイルされたクエリーの実行時にパラメーターとして渡されます。クエリー自体とそのアクセス・プランは、DB2 が 1 度キャッシュすれば、後はクエリーを実行するたびに毎回使用することができます。この手法によってパフォーマンスが向上する理由は、主に、データベースが同じクエリーに対して何度も実行プランを計算することによって生じるオーバーヘッドが一切なくなるからです。このようなクエリー (あるいは、これよりも複雑なクエリー) を何百回、何千回と繰り返し実行してみると、パラメーター化クエリーを使用した場合と使用しない場合の違いはかなり顕著になってきます。

Rails アプリケーションでデータベース接続およびクエリーを処理するために使用するのは、ORM (Object-Relational Mapper) フレームワークの ActiveRecord です。ActiveRecord は、デフォルトではパラメーター化クエリーをサポートしません。これはつまり、各クエリーは、ユーザーの入力 (通常は十分サニタイジングされた入力) を動的にマージすることによって作成され、それぞれ独自のクエリーとして実行されることを意味します。例えば、ActiveRecord は以下のメソッド呼び出し (リスト 3) を前述の個々のクエリー (リスト 1 を参照) に変換します。

リスト 3. リスト 1 に相当する ActiveRecord
Person.find_by_name('Antonio')
Person.find_by_name('Leon')
Person.find_by_name('Praveen')
Person.find_by_name('Mario')

DB2 on Rails の最新 Ruby gem (ibm_db) では、ActiveRecord アダプター (IBM が DB2 用に開発) が上記のクエリーのそれぞれを処理して、すぐに再利用できる単一のパラメーター化クエリーを作成します。パフォーマンスはさておき、パラメーター化クエリーは SQL インジェクションによる攻撃を防ぐ上でも理想的です。パラメーターはクエリーに渡される値として見なされるため、クエリー自体の構造には何の影響も及ぼしません。したがって、悪意のあるユーザーが引用符と特殊文字を用いて動的にクエリーの構造を変更しようとしても、その巧妙なトリックはまったくの無駄となるわけです。

ユーザー入力のサニタイジングに関して言えば、開発者がフレームワークによって提供された適切なツールを慎重に使用する限り、ActiveRecord はこれまでも優れた成果を上げてきました。例えば以下の命令 (リスト 4) は、ActiveRecord が直接的な SQL インジェクションに対して適切なサニタイジングを行っています。

リスト 4. ActiveRecord での安全な入力処理
User.find(:all, :conditions => ["role = ? AND age > ?", role, age])

一方、それほど慎重ではない開発者が (値を直接組み込むつもりで) リスト 5 のテキストを入力したとすると、その呼び出しは SQL インジェクション攻撃にさらされることになります。

リスト 5. ActiveRecord での安全ではない入力処理
# Don't do this
User.find(:all, :conditions => "role = '#{role}' AND age > #{age}")

上記のような場合、クエリーは実際に、role と age の値に基づいて動的に作成されます。例えば age が “18 OR 1=1” というストリングだとすると、クエリーは以下のようなものになります。

リスト 6. SQL インジェクション攻撃
SELECT * FROM users WHERE role = 'user' AND age > 18 OR 1=1

その結果、公開するつもりでなかったレコードが取得されることになってしまいます。

この理由 1 つをとっても、開発者は常に、ActiveRecord が入力のサニタイジングをするために提供している安全な方法 (リスト 4 の方法) を選ばなければなりません。DB2 on Rails の新しいエディションでは、こうした「安全」な呼び出しが、SQL インジェクション攻撃に影響されないパラメーター化クエリーに変換されるおかげで、セキュリティーの層がさらに追加されます。DB2 を使っていれば、賢いハッカーが ActiveRecord で採用されているサニタイジングのプロセスをどうにか迂回して文字や悪意のある引用符を追加したとしても、クエリーの本質が変更されることはないと安心することができるのです。


ibm_db 2.0.0 のインストールまたは、ibm_db 2.0.0 へのアップグレード

Rails でパラメーター化クエリーを利用するためにアプリケーション内のコードを変更する必要は一切ありません。以下の 2 つの簡単なステップに従うだけで、この新しい機能を有効にすることができます。

  • DB2 on Rails の最新バージョンの Ruby gem (ibm_db として知られています) をインストールする
  • アプリケーションのデータベース構成ファイルを変更する

早速、上記のステップを実践してみましょう。

Microsoft® Windows® を使用している場合は、コマンド・プロンプトから以下のコマンドを実行するだけで gem をインストールすることができます。

リスト 7. Windows での ibm_db gem のインストール
C:\> gem install ibm_db

Linux® または UNIX® を使用している場合、gem をインストールする前に、以下のように開発用ヘッダー・ファイルが置かれている場所を指定する必要があります。

リスト 8. Linux/AIX® での ibm_db gem のインストール
$ sudo -s
$ export IBM_DB_INCLUDE=/home/db2inst1/sqllib/include
$ export IBM_DB_LIB=/home/db2inst1/sqllib/lib
$ . /home/db2inst1/sqllib/db2profile
$ gem install ibm_db

(64-bit 版を使用している場合は、代わりに lib64 ライブラリーの場所を指定してください。)

Mac OS X 10.5 (Leopard) を使用している開発者は、上記と同じような方法で gem をインストールすることができます。

リスト 9. Mac OS X 10.5 での ibm_db gem のインストール
$ sudo -s
$ export IBM_DB_LIB=/Users/<username>/sqllib/lib
$ export IBM_DB_INCLUDE=/Users/<username>/sqllib/include
$ . /Users/<username>/sqllib/db2profile
$ gem install ibm_db

Mac OS X 10.6 (Snow Leopard) を使用している場合は、以下を実行してください。

リスト 10. Mac OS X 10.6 での ibm_db gem のインストール
$ sudo -s
$ export IBM_DB_LIB=/Users/<username>/sqllib/lib64
$ export IBM_DB_INCLUDE=/Users/<username>/sqllib/include
$ export ARCHFLAGS="-arch x86_64"
$ gem install ibm_db

以上の命令はいずれも、gem をインストールするか、あるいは gem の古いバージョンがインストールされている場合には最新バージョンに更新します。インストール済み RubyGems バージョンで gem install コマンドが正常に実行されない場合には、以下のコマンドを実行して、パッケージ・マネージャー自体を更新してください。

リスト 11. RubyGems の更新
$ gem update --system

既存の Rails アプリケーションでパラメーター化クエリーを有効にする

Rails アプリケーションでは、データベース構成が config/database.yml ファイルに保存されます。特定のアプリケーションでパラメーター化クエリーを有効にするには、このファイルで parameterized パラメーターを true に設定します。以下にその例を記載します。

リスト 12. 単一環境での構成スニペットの例
production:
  adapter: ibm_db
  username: db2inst1
  password: secret
  database: mydb
  parameterized: true

この構成パラメーターが true に設定されない場合、DB2 の ActiveRecord アダプターは引き続き以前のバージョンと同じ動作をします。つまり、通常のクエリーをパラメーター化クエリーに変換しません。こうすることで、パフォーマンスの向上を即座に評価し、本番モードでもこのオプションを有効にしておくか、それとも無効にするべきかを決定するのが容易になります。


パフォーマンスについての検討事項

必ず自分でベンチマークを実行しなければならないのは確かですが、私たちのラボで行ったテストでは、パラメーター化クエリーを有効にするとパフォーマンスが大幅に改善することが証明されました。Prepared Statement を使用するメリットは、ワークロードの増加によって明らかに示されます。図 1 のグラフに、パラメーター化クエリー (Parameterized queries) を有効にした場合と無効にした場合、およびステートメント・コンセントレーター (Statement Concentrator) を使用した場合に、比較的重いワークロードのテストをした結果を示します。

図1. ベンチマークの結果
ベンチマークの結果を示す図。トランザクションの所要時間が比較されています。

このグラフを見るとわかるように、パラメーター化クエリー (Parameterized queries) を有効にすると、データベースのスループットが約 30% から40% 増加します。ステートメント・コンセントレーター (Statement Concentrator) を有効にした場合と比べても、15% から 25% 上回っています。中間段階として見なすことのできるステートメント・コンセントレーターは、Prepared Statement のメリットをデータ・サーバーのレベルで実現しようとする機能で、その方法は、同一のクエリーで同じアクセス・プランを共有 (リテラル値を保存) することによってコンパイルのオーバーヘッドを減らすというものです。この優れた機能は DB2 9.7 でのみ使用可能です。DB2 9.7 では、特定の開発フレームワークにパラメーター化クエリーを使用できない場合、この機能を有効にしなければなりませんが、嬉しいことに DB2 on Rails を使用する開発者は今後、この機能を有効にするかどうかを心配する必要がなくなります。


まとめ

Rails が持つ数少ない (けれども非常に重要な) 欠点の 1 つに対処する新バージョンの ibm_db gem の発表は、Rails の単純さと世界的なデータベースが持つ威力を組み合わせたいと思っている人にとって朗報となるはずです。DB2 ではネイティブ XML ストレージ (pureXML) をはじめとする多数の強力な機能を備えており、企業レベルの Rails アプリケーションをスケーラブルにする方法を提供しています。しかも完全に無料のバージョンである DB2 Express-C のおかげで、DB2 では他の追従を許さない価格でこれを実現できます。

参考文献

学ぶために

製品や技術を入手するために

  • DB2 Express-C: DB2 Express-C をダウンロードしてください。完全にライセンス交付されたこの製品は、コミュニティーに無料で提供されています。
  • IBM Data Studio (US): Data Studio をダウンロードしてください。
  • 次期開発プロジェクトの構築に、developerWorks から直接ダウンロードできる IBM の試用版ソフトウェアをご利用ください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management, Open source, Web development
ArticleID=480122
ArticleTitle=パラメーター化クエリーを使用して DB2 Ruby on Rails アプリケーションのセキュリティーとパフォーマンスを強化する
publish-date=01212010