オープンソースのERP iDempiereのSQLインジェクションへの対策について、調査及び研究し、その成果をまとめています。
SQLインジェクションとは
IPA 「安全なウエブサイトの作り方 改訂第7版」から引用
データベースと連携したウェブアプリケーションの多くは、利用者から入力情報を基にSQL文(データベースへの命令文)を組み立てています。ここで、SQL文の組み立て方法に問題がある場合、攻撃によってデータベースの不正利用をまねく可能性があります。このような問題を「SQLインジェクションの脆弱性」と呼び、問題を悪用した攻撃を、「SQLインジェクション攻撃」と呼びます。
SQLインジェクションへの対策
IPA 「安全なウエブサイトの作り方 改訂第7版」では、SQLインジェクションの根本的解決法として下記の方法を記載しています。
SQL文の組み立ては全てプレースホルダで実装する
SQLには 通常 、プレースホルダを用いてSQL文を組み立てる仕組みがあります。SQL文の雛形の中に変数の場所を示す記号(プレースホルダ)を置いて、後に、そこに実際の値を機械的な処理で割り当てるものです。ウエブアプリケーションで直接、文字列連結処理によってSQL文を組み立てる方法に比べて、プレースホルダでは、機械的な処理でSQL文が組み立てられるので、SQLインジェクションの脆弱性を解消できます。
プレースホルダに実際の値を割り当てる処理をバインドと呼びます。バインドの方式には、プレースホルダのままSQL文をコンパイルしておき、データベースエンジン側で値を割り当てる方式(静的プレースホルダ)と、アプリケーション側のデータベース接続ライブラリ内で値をエスケープ処理してプレースホルダにはめ込む方式(動的プレースホルダ)があります。静的プレースホルダは、SQLのISO/JIS規格では、準備された文(Prepared Statement)と呼ばれます。
どちらを用いてもSQLインジェクション脆弱性を解消できますが、原理的にはSQLインジェクション脆弱性の可能性がなくなるという点で、静的プレースホルダの方が優ります。
上記の引用文から、SQLインジェクション対策として、プリペアド文(Prepared Statement)でプレースホルダを使用する方法が有効である事が読み取れます。そして、プレースホルダには、動的プレースホルダと静的プレースホルダがあり、静的プレースホルダがより好ましい事がわかります。
SQLはプリペアド文でプレースホルダを使用する
静的プレースホルダがより好ましい
そして静的プレースホルダとは、データベースエンジン側で、プレースホルダの値を割り当てる方式である旨の説明が記載されています。
iDempiereのSQLインジェクション対策
SQLインジェクションとその対策について理解したところで、次にiDempiereのSQLインジェクション対策について調査及び研究します。
iDempiereはプリペアド文を使用してSQLを実行している
iDempiereではプロジェクトの初期のチケットでSQLの実行の書き方を統一しています。このチケットはメモリーリークやコネクションリークを無くすためにSQLの書き方を統一させているのですが、SQLの実行に際してプリペアド文を使用しているのが確認できます。
参考サイト
iDempiereではこのチケットのようにSQLの書き方をプリペアド文を使用して、finally句で閉じる記述に統一していますので、iDempiereのアドオン機能を開発する場合は、このお作法を遵守する事で、SQLインジェクション対策になるだけでなく、システムの安定稼働にもつながります。
iDempiereのモデル駆動アーキテクチャの中心となるPOクラスでもプリペアド文にきりかえられているのが過去のチケットからも確認する事ができます。
プリペアド文を使用しているのは、PostgreSQLのSQL実行ログからも確認する事ができます。postgresql.confの"log_statement"のパラメータをallにしてpostgreSQLを再起動するとログをpg_logディレクトリに実行したSQLのログを出力してくれます。そのログの中には、"execute"と至る所に表示されている事からプリペアド文が使用されているのがわかります。
参考サイト
上記のログは、受注伝票のウィンドウを開いた時に発行されているSQLの一文です。プレースホルダを使用したプリペアド文である事が確認できます。
あとはこのプレースホルダが、"動的プレ―スホルダ"か、"静的プレースホルダ"か気になる所ですね。IPAの「安全なウエブサイトの作り方 改訂第7版」では"データベースエンジン側で値を割り当てる方式"と説明しています。PostgreSQLの上記ログを見てもわかるように、PosgreSQL側で値を割り当てています(iDempiere5.1のPostgreSQLのドライバーは”postgresql-42.1.4.jar”を使用)。
この事からJavaでPostgreSQLのドライバーを使用しているのであれば、基本的に静的プレースホルダを使用する事ができると考えて良いのではないかと思います。
プリペアド文とプレースホルダのエスケープ処理の確認
プリペアド文とプレースホルダを使用したエスケープ処理について確認します。
ウィンドウの検索アイコン
ウィンドウの検索アイコンで、説明欄に「';DELETE FROM C_Order--」と入力して実行してみます。
ログを確認すると、検索文字列を'(シングルクオテーション)で囲んでくれて、さらに検索文字列に含まれる'(シングルクオテーション)を'(シングルクオテーション)でエスケープ処理してくれているのが確認できます。
ただしこの場合、PostgreSQLのログではプレースホルダは使用されていません。ウィンドウの検索アイコンでの検索はSQLの組み立てからエスケープ処理までiDempiere側で実装されており、動的プレースホルダと考える事ができるかなと思います。
検索ウィンドウ
同じように検索ウィンドウでも確認してみます。
検索ウィンドウでのSQLの実行には、エスケープ処理がされており、さらにプレースホルダも使用されているのが確認できます。
検索ウィンドウでのSQLの実行は、静的プレースホルダを使用していると考えて良いのではないかと思います。
【まとめ】iDempiereのSQLインジェクション対策
- iDempiereはSQLの実行にはプリペアド文を使用しており、プレースホルダについては、動的プレースホルダと静的プレースホルダのハイブリッド型と考える事ができる。ユーザー入力した値をそのままSQLに使用するような事は基本的には無く(注:すべてのSQLを確認したわけではないので"基本的には…"としています)、SQLインジェクションに対して耐性があると言って良いのではないかと思われる。
- iDempiereのアドン機能の開発者はSQLを実行する際には静的プレースホルダを使用するのがより好ましい。