SHOEISHA iD

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

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

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

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

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

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


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

 zip2addressの改修が終わったので、今度はzip2addressを呼び出す際のSQLインジェクション脆弱性について検討しましょう。先のストアドプロシージャ呼び出しはプレースホルダ「?」を使っていましたが、文字列連結によりパラメータを渡す実装もありえます。今度は言語をPHPとPDO(PHP Data Object)に変更した実装例を以下に示します。まずは正常系です。PDOにはプリペアードステートメント呼び出し用の専用メソッドはないため、通常のqueryメソッドで呼び出しています。

$dbh = new PDO(接続文字列, ユーザ名, バスワード);
$zipcode = '1420063';
$country = 'jp';
$sql  = "CALL zip2address('$zipcode', '$country')";
$stmt = $dbh->query($sql);
// 以下略

 これを実行すると、「東京都港区麻布十番」が返ります。

 次に攻撃例です。$countryを以下のように指定します。

$country = "jp');DELETE FROM zip -- ";

 この場合、呼び出されるSQL文は下記のとおりです。第2のSQL文としてDELETE文が追加されています。

CALL zip2address('1420063', 'jp');DELETE FROM zip -- ')

 これを実行すると、zipテーブルの内容がすべて削除されてしまいます。SQLインジェクション攻撃の成功です。

 このような攻撃を避けるためには、プレースホルダを使います。Javaの例は先ほど示しましたが、PHPとPDOの場合は以下のように記述します。これによりSQLインジェクション脆弱性は解消されています。

$stmt = $dbh->prepare("CALL zip2address(?, ?)");
$stmt->bindValue(1, $zipcode);
$stmt->bindValue(2, $country);
// ストアドプロシージャをコールします
$stmt->execute();

エスケープによる対策は難易度が高い

 さて、ここでSQL Injection Prevention Cheat Sheetに話を戻しましょう。このドキュメントは第3の選択肢として、「Escaping All User Supplied Input(すべてのユーザ入力のエスケープ)」を挙げています。しかし、中身を読むと、手放しでエスケープによる方法を推奨しているわけではありません。該当箇所の冒頭には以下のように説明されています。

This second technique is to escape user input before putting it in a query. However, this methodology is frail compared to using parameterized queries and we cannot guarantee it will prevent all SQL Injection in all situations. This technique should only be used, with caution, to retrofit legacy code in a cost effective way. Applications built from scratch, or applications requiring low risk tolerance should be built or re-written using parameterized queries.

【参考訳】この第2の手法は、ユーザ入力をクエリ内に入れる前にエスケープすることです。しかし、この方法論はパラメータ化クエリ(訳注:プレースホルダを用いたSQL呼び出しのこと)を用いる場合と比較して弱く、我々はそれがすべての状況ですべてのSQLインジェクションを妨げると保証することができません。この手法は、既存のコードを費用対効果がよい方法で改修する場合に限り、用心深く用いられるべきです。スクラッチから構築するアプリケーションや、リスク許容度の低いアプリケーションは、パラメータ化クエリを用いて構築あるいは書き換えされるべきです。

 私は上記見解に同意します。エスケープ手法は、既存のコードに脆弱性が発見された場合で、大幅な改修が難しい場合に限って使用すべきです。その場合は入力値検証など他の手法もあわせて用いることで安全性を高めることを推奨します。

結局のところSQLインジェクション対策の極意は、SQL文を動的に組み立てないこと

 本稿ではストアドプロシージャのサンプルを用いながらSQLインジェクション脆弱性の例を示しました。脆弱性が混入している箇所はいずれもSQL文を動的に組み立てているところに問題がありました。従って、動的なSQL文組み立てを避け、変数(パラメータ)の指定をプレースホルダにより行うことを徹底すれば、SQLインジェクション脆弱性を解消することができます。それは、通常のSQL呼び出しの場合、ストアドプロシージャの定義の場合、ストアドプロシージャの呼び出しの場合のいずれにも共通する原則です。

 現実のアプリケーションでは、SQL文の構造が動的に変化する場合も多いでしょう。その一例が、冒頭の例に示したように表名(の一部)を外部から指定するようなケースです。そのような場合は、外部由来の値を直接SQL文に混入させる実装をやめて、表名や列名として有効な値を配列など安全な場所に保持しておき、SQL文にはこの配列経由で指定することにすれば、外部由来の値を直接SQL文に混入してしまうことは避けられます。

 以上を3か条の原則にまとめましょう。

  • 原則としてSQL文を動的に組み立てない
  • やむを得ずSQL文を動的に組み立てる場合は、列名やテーブル名などを補助的な配列などにより指定することで、外部由来の値をSQL文に混ぜない
  • 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」など、さまざまなカンファレンスを企画・運営しています。

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

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

メールバックナンバー