SQLの条件節が動的に構成されることを考慮した データベース接続APIの設計 2007/11/02 東京大学生産技術研究所 渡邉 悠
Agenda SQLインジェクション攻撃の概要 ユーザの入力を正しく処理するための技術 提案手法 まとめ SQLとは 脆弱性の原因 ユーザの入力を正しく処理するための技術 エスケープ処理 バインディングメカニズム 提案手法 まとめ
SQL リレーショナルデータベースに対してデータの操作や検索を行うための問い合わせ言語 Webアプリケーションなどではユーザの入力から動的に生成される 値の登録 検索条件の指定 よく利用されるSQL select(検索), update(更新), insert(挿入), delete(削除) ユーザ データベース select * from users where id=‘taro’ id = “taro” Webサーバ HTML taroに関する情報
SQLインジェクション攻撃 例えば以下のようなプログラムがある場合 正常な実行 インジェクション攻撃 sql = “select * from users where id = ‘” + request[“id”] + “ ’ ”; execute(sql); 正常な実行 request[“id”]がtaroならば実行されるSQLは select * from users where id = ‘taro’となる。 usersテーブルの中のidがtaroのものがレスポンスとして得られる インジェクション攻撃 request[“id”]が「taro’ or ‘a’ = ‘a」であった場合 select * from users where id = ‘taro’ or ‘a’ = ‘a’が実行される 開発者が想定したのと異なるSQLが実行される すべてのユーザに関する情報が漏洩する危険性
インジェクション攻撃の原因 プログラマが生成されると期待しているSQLと実際に生成されるSQLの間にギャップが存在する SQL 「“select * from users where id = ‘“ + id + “’”」というプログラムはプログラマが本当に生成したいSQLの集合を正しく表現できていない あるプログラミング言語の中で単純な文字列処理を用いて、他の言語のプログラムを動的に構築する際に常に存在する問題 クロスサイトスクリプティング、OSコマンドインジェクション みんなが大好きなXMLでも Directory Traversal攻撃 もある意味似ている 想定外のSQLクエリの中には危険なクエリが存在する可能性 攻撃者はそういったクエリを生成・実行させようとする SQL インジェクション 攻撃 想定されたSQL 実際に生成 されるSQL
正しいプログラムの構築 ユーザの入力を適切に処理し、プログラマが想定した通りのSQLを生成するための技術 サニタイジング(エスケープ処理) もっともレガシーな方法 バインディングメカニズム(準備済みステートメント) より進んだ手法 O/Rマッパー データベース接続部分(SQL生成部分)を自動生成するツール 今回は省略 O/Rマッパーに対する考えを聞きたい人は質問してください
Agenda SQLインジェクション攻撃の概要 ユーザの入力を正しく処理するための技術 提案手法 まとめ SQLとは 脆弱性の原因 ユーザの入力を正しく処理するための技術 エスケープ処理 バインディングメカニズム 提案手法 まとめ
もっともレガシーで安直な解決策 サニタイジング,エスケープ処理を行う 単純な文字列処理の問題点 ユーザの入力に含まれる特殊文字を無害なものに変換 単純な文字列処理の問題点 正しい処理の仕方は、表現したいものの型・表現するものの位置によって異なる ある型は、SQLではどういう風に表現するのか等の理解が不可欠 SQLの細かい話に関してプログラマが詳しい必要があるが現実には難しい 利用するデータベースの実装によって異なる 勉強する必要の無いことは勉強したくない 関数escapeをはさむだけ 文字列変換用の関数を呼び出すのを忘れる危険性 より高度なSQL構築手段が必要
バインディングメカニズム 例 解説 sql = “select * from users where id = @id” sqlCmd = new SqlCommand(sql); sqlCmd.Parameters.Add(“@id”, id); sqlCmd.Execute(); 解説 値を代入する部分をとりあえず、@idなどして記述しておく 値の代わりに書いておく文字のことを、スペースホルダという スペースホルダを含む文字列を利用してコマンドオブジェクトを作成 sqlCmd.Parameters.Add(“@id”,id)などにより、「@id」に「id」の中の値を代入する。(バインディング) @idがidの文字列で置換されるのではない @idがidの文字列をSQL上であらわす文字列リテラルに置換される SQLの実行
バインディングメカニズムの利点と問題点 利点 問題点 ユーザの入力が値の型にあわせて適切な文字列に変換される あらゆる型の値について、プログラマは同じ枠組みが使える 対象とするSQLにそれほど詳しくない場合でも安全なコードが記述できる 関数の書き忘れなどの不注意で脆弱性を埋め込んでしまう危険性が低い 問題点 「値」の動的な決定にしか利用できない 動的にSQLの構文木の構造が変化する場合に対応できない ユーザの入力によって条件が発生したり、消えたりする場合 ユーザが入力した複数の値を検索条件とする場合
提案手法 構文木の構造が動的に決定される場合にも安全・効率的にSQLを構築可能なAPIの設計 対象 性質 動的に有無が変化する条件(optional condition) 条件の繰り返し 性質 単純な文字列処理を使うことなく上の2つを取り扱うことのできる枠組み バインディングメカニズムの拡張
unbounded 特殊な値「unbounded」の導入 次のような構文木の変換規則を持つ プレースホルダ(@id)に何もバインドされていない状態を表す特殊値 次のような構文木の変換規則を持つ 二項演算子andおよびorの子要素の一方がunboundedであった場合、他方の子要素に変換する。両方の子要素がunboundedである場合はunboundedに変換される 「true and unbounded」 → 「true」 unboundedを子要素に持つその他の演算はすべてunboundedに変換される 「id = unbounded」→「unbounded」 「function( unbounded)」→「unbounded」
実行時に有無が決定される条件の取り扱い すべての条件が存在する場合のSQLをスペースホルダを利用して記述しておく select * from users where name = @name and birthday >= @birthday_from and birthday <= @birthday_to 必要な条件には値をバインドする。不要な条件にはunboundedをバインドする select * from users where name = ‘foo’ and birthday >= unbounded and birthday <= ’99/12/31’ 上記で述べたunboundedの変換規則を利用して変換を行うことで、不要な条件の除去されたSQLが生成される select * from users where name = ‘foo’ and birthday <= ’99/12/31’
変換の様子 (1/2) name = @name and birthday >= @birthday_from and birthday <= @birthday_to And = And name @name >= <= birthday @birthday from birthday @birthday to
変換の様子 (2/2) name = ‘foo’ and birthday <= ’99/12/31’ And = And name >= <= birthday unbound birthday 99/12/31
提案手法 構文木の構造が動的に決定される場合にも安全・効率的にSQLを構築可能なAPIの設計 対象 性質 動的に有無が変化する条件(optional condition) 条件の繰り返し 性質 単純な文字列処理を使うことなく上の2つを取り扱うことのできる枠組み バインディングメカニズムの拡張
条件の繰り返しの取り扱い 特殊な演算:mapreduceの導入 文法 mapreduce(@element in @list, binary-operator, expression) 動作 実行時に、@listに配列をバインドする @listにバインドされた配列の各要素を、expression中の@elementにバインドしたものを作成する これを、binary-operatorで結合したものに変換する バインドされた配列の長さが0である場合には、unboundedに変換される
条件の繰り返しの取り扱い:例 配列に含まれるいずれかの値にidが一致するユーザの情報を取得 事前に用意するSQL “select * from users where mapreduce(@id in @ids, or, id = @id)” 与えられた配列が{“foo”, “bar”, “baz” }であった場合 select * from users where id = “foo” or id = “bar” or id = “baz” が生成される
実装上の留意点 実装方法の可能性 今回の提案手法 APIの拡張で実装できることのメリット データベース接続用のAPIで実装する データベース自体(RDBMS)を拡張し実装する 今回の提案手法 構文木の操作による手法 データベースのコア部分(検索などの実行部分)には手をいれていない データベース自体に変更を加えなくても、データベース接続用のAPIを拡張するだけで実現可能 APIの拡張で実装できることのメリット 新しくアプリケーションを構築する際に、このAPIを利用するだけでよい。 導入にあたり既存のシステムの変更が不要 導入コストが小さい 既存のシステムには副作用を及ぼさない 導入によるリスクが小さい
まとめ インジェクション攻撃に対する脆弱性はなぜ生じるのか ユーザの入力を正しく処理するAPI・ライブラリについて SQL(に限らずあらゆる言語)を動的に文字列の連結で構築するプログラムはプログラマの意図を正しく表現できない プログラマの意図を正しく表現できるように設計されたライブラリが欲しい ユーザの入力を正しく処理するAPI・ライブラリについて エスケープ処理・サニタイジング エスケープ処理の問題点の指摘 バインディング・メカニズム 構文木が動的に変化する場合、文字列の結合による構築が避けられない バインディング・メカニズムを拡張した提案手法 「動的に有無の変化する条件」と「条件の繰り返し」を簡単・効率的に取り扱えるAPIの提案
Future Work プログラマが利用しなければ何の効果も無い より使いやすければ使われるわけではない この部分を解決します 実装
参考文献 [1] ADO.NET. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnanchor/html/adonetanchor.asp [2] Hibernate. http://www.hibernate.org/. [3] IPA. 情報セキュリティ白書2007 年版. http://www.ipa.go.jp/security/vuln/20070309 ISwhitepaper.html. [4] JDBC. http://java.sun.com/javase/technologies/database/. [5] Perl. taint mode. http://www.perl.org/. [6] Frank S. Rietta. Application layer intrusion detection for sql injection. In ACM-SE 44: Proceedings of the 44th annual Southeast regional conference, pp. 531–536, New York, NY, USA, 2006. ACM Press. [7] Zope. http://www.zope.org/.
終わり 質問・意見をお願いいたします 提案手法細かい点についてもいいですが 提案手法の目指すところや、インジェクション系脆弱性に立ち向かうときの考え方などについて御意見・ご感想を聞かせていただけると嬉しく思います