SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

OWASPでビルトイン・セキュリティ

SQLインジェクション対策の極意はSQL文を組み立てないことにあり

OWASPでビルトイン・セキュリティ 第4回


ストアドプロシージャとは

 ストアドプロシージャとは、データベースに対する複数の操作をまとめた手続きや関数のことです。一般的なプログラミング言語では、サブルーチンや関数に相当するものです。一般的なSQL問い合わせでは条件分岐や繰り返し処理はできませんが、ストアドプロシージャは、通常の手続き型言語のように分岐や繰り返しが可能です。

 ストアドプロシージャは、データベースサーバー内で処理が閉じているためネットワーク処理に対するオーバーヘッドがないこと、またSQL文の構文解析や問い合わせの実行計画作成をストアドプロシージャ定義の際に済ませておけることから、実行効率がよいという特徴があります。

なぜストアドプロシージャがSQLインジェクション対策になるのか

 なぜストアドプロシージャがSQLインジェクション対策になるのでしょうか。その理由は、先に述べたように、ストアドプロシージャの定義時に構文解析をあらかじめ行っている点にあります。通常のSQL呼び出しでは、SQL文を呼び出すたびにSQL文の構文解析を行いますが、その際、外部からのパラメータによりSQL文の意味が変わってしまう場合があります。この状況がSQLインジェクション脆弱性です。

 しかし、ストアドプロシージャは、その定義時にあらかじめ構文解析を行っていることから、外部からのパラメータなどによりSQL文の意味が変わることはないはずです。そして、これは先に紹介した選択肢1のプリペアードステートメントがSQLインジェクション対策になる理由でもあります。プリペアードステートメントは日本語訳の用語では「準備された文」ですが、この「準備」はあらかじめSQL文を構文解析することを意味します。従って、プリペアードステートメントの使用もSQLインジェクション対策として有効とされています。

ストアドプロシージャを使いさえすればSQLインジェクション対策になるわけではない

 それでは、ストアドプロシージャを使いさえすれば、SQLインジェクション脆弱性が入り込む心配はないのでしょうか。実はそうではありません。ストアドプロシージャを使っていても、SQLインジェクション脆弱性が入り込む余地はあります。それは以下の2つの場合です。

  • ストアドプロシージャ中でSQL文を動的に組み立てる場合
  • ストアドプロシージャを呼び出す際にSQL文を動的に組み立てる場合

 以下、ストアドプロシージャを使っていてもSQLインジェクション脆弱性が混入する例を、サンプルプログラムを用いて説明します。

ストアドプロシージャの内部でSQL文を動的に組み立てるサンプル

 まずは、ストアドプロシージャのサンプルを紹介しましょう。以下は、各国の郵便番号(Zipコード)から住所を求めるストアドプロシージャです。郵便番号と住所の対応表は、国別のテーブルに掲載されており、テーブル名はzip_<国名コード>となっています。日本の場合はzip_jp、米国の場合はzip_usという具合です。このテーブルには、郵便番号(zipcode)と住所(address)という2つの列があります。以下のMySQL向けストアドプロシージャzip2addressは、郵便番号zipcodeと国名コードcountryを受け取り、住所を問い合わせます。

001|CREATE PROCEDURE zip2address(IN zipcode TEXT, IN country TEXT)
002|BEGIN
003|  SET @zipcode = zipcode;
004|  SET @sql_str = CONCAT('SELECT address FROM zip_', country, ' WHERE zipcode=?');
005|  PREPARE stmt1 FROM @sql_str;
006|  EXECUTE stmt1 USING @zipcode;
007|  DEALLOCATE PREPARE stmt1;
008|END
  • 1行目:zip2addressプロシージャを定義します。入力引数としてzipcodeとcountryを宣言します。
  • 2行目:BEGINで複合ステートメントを開始します(トランザクションの開始ではありません)。
  • 3行目:ローカル変数@zipcodeに引数zipcodeの値を割り当てます。
  • 4行目:テーブル名断片zip_に国名コードを連結するために、CONCAT関数を用いて文字列連結を行い、SQL文文字列を組み立て、ローカル変数@sql_strに代入します。SQL文中の「?」はプレースホルダと言って、パラメータの位置を確保するものです。
  • 5行目:先のSQL文字列からSQL文を準備(prepare)します。
  • 6行目:プレースホルダ(?)に@zipcodeの値を割り当ててSQL文を実行します
  • 7行目:SQL文を開放します
  • 8行目:複合ステートメントの終わりです

 このストアドプロシージャzip2addressの呼び出し例をmysqlコマンドで行った例を示します。

mysql> CALL zip2address('1060045', 'jp');
+-----------------------------+
| address                     |
+-----------------------------+
| 東京都港区麻布十番          |
+-----------------------------+

 JavaからJDBCを用いて同じストアドプロシージャ呼び出しをする例を以下に示します(主要部のみ)。

Class.forName("com.mysql.jdbc.Driver");  // MySQL用JDBCドライバ
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/…    // 接続
CallableStatement statement = con.prepareCall("CALL zip2address(?, ?)");
statement.setString(1, "1420063"); // zipcodeの値を指定
statement.setString(2, "jp");       // country の値を指定
ResultSet rs = statement.executeQuery();  // 問い合わせ実行

 SQL文の準備にprepareStatementではなくストアドプロシージャ向けのpreapareCallを使う以外は通常のSQL文呼び出しと同じです。結果は先と同じなので省略します。

次のページ
脆弱性の確認

この記事は参考になりましたか?

OWASPでビルトイン・セキュリティ連載記事一覧

もっと読む

この記事の著者

徳丸 浩(OWASP Japan)(トクマル ヒロシ)

1985年京セラ株式会社入社、1995年、京セラコミュニケーションシステム株式会社(KCCS)転籍。携帯電話向けインフラ、プラットフォームの企画・開発を担当。Webアプリケーションのセキュリティ問題に直面し、研究、社内展開、寄稿などを開始する。2004年に、KCCS社内ベンチャーとしてWebアプリケ...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/9204 2016/02/04 21:21

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー