SQLインジェクションの脅威と対策(安全なウェブサイトの作り方)


はじめに

本記事は安全なウェブサイトの作り方(IPA)の第一章「ウェブアプリケーションのセキュリティ実装」に解説を加えた記事です。
長いので分割して投稿しています。
画像はすべて上記サイトから引用してます。

目次

1.概要
2.脅威
3.根本的解決
4.保険的対策

SQLインジェクション

概要

データベースに対してSQL文を送信できるwebアプリケーションに対して、不正なSQL文を組み立てて、本来得られない情報を入手・改ざんする攻撃

脅威

・非公開情報の閲覧
・DB内情報の改ざん・消去
・不正ログイン
・ストアドプロシージャを利用したOSコマンドの実行

対象

DBを利用するすべてのwebアプリケーション
特に個人情報を扱うDBを利用するwebサイト

根本的解決

(1)プレースホルダの利用

変数を入れたい場所にプレースホルダと呼ばれる仮の記号を入れておきます。後で、実際の変数とプレースホルダを紐づけます(バインド)。これによって、SQL命令に関わるような「特殊文字」は無効化されます。

具体的な手順

下記の様なSQLを実行するPHPプログラムがあったとします。$nameはユーザーが入力する値です。
$sql = "SELECT * FROM user WHERE name='$name'";

ここに、不正な文字を入力すると、不正な命令が実行されてしまいます。
下の例では、「';DELETE FROM user--」という値を入力することで、userテーブルの値がすべて削除されてしまう不正なSQL文になってしまいました。

$sql = "SELECT * FROM user WHERE name='';DELETE FROM user--";

これを防ぐために、$nameをプレースホルダと呼ばれる文字列(?:name@nameなど)で仮置きしておきます。
※OracleのSQLやPL/SQLではコロン(:)、他の言語ではクエスチョンマーク(?)等を使用します。[2]
下の例では、:nameを使います。

$sql = "SELECT * FROM user WHERE name=:name";

最後に、プレースホルダと実際の変数をバインドします。
バインドに用いる関数は言語ごとに存在しますが、今回はPHPbindValue()関数を使います。

bindValue(':name', $name, PDO::PARAM_STR);

これで、:name$nameがバインドされました。

参考文献:[1] 
----------------------------

(2)エスケープ処理の実装

前述のプレースホルダを用いずに、文字列連結によりSQL文の組み立てを行いたい場合は、可変の値を定数(リテラル)として埋め込むことが推奨されます。
具体的には、入力値が文字列型の場合は特別な意味を持つ記号をエスケープ処理する(たとえば、「'」→「''」、「\」→「\」等)、入力値が数値型であれば、数値型へのキャストを行って、数値リテラルであることを確実にするといった対処が挙げられます。

(3)ウェブアプリケーションに渡されるパラメータにSQL文を直接指定しない

HTTPのhiddenパラメータ等からSQL文を直接受け取るようになっているといくらでも改ざんが可能です。やめましょう。

保険的対策

(1)エラーメッセージを隠す

エラーメッセージは開発者・利用者にとっては有用な情報である一方、攻撃者にとっても有用な情報になります。また、エラーメッセージに漏洩してはいけない情報が表示されてしまうこともあるため、エラーメッセージは書き換えて表示する・そもそも表示させないことが推奨されます。

(2)DBに適切な権限の設定

想定されるSQL文に必要な権限のみ与えましょう。

リンク

1.SQLインジェクション←本記事
2.OSコマンド・インジェクション
3.パス名パラメータの未チェック/ディレクトリ・トラバーサル
4.セッション管理の不備
5.XSS(クロスサイトスクリプティング)
6.CSRF(クロスサイト・リクエスト・フォージェリ)
7.HTTPヘッダ・インジェクション
8.メールヘッダ・インジェクション
9.クリックジャッキング
10.バッファオーバーフロー
11.アクセス制御や認可制御の欠落

参考文献

[1]フルスタックエンジニアのノウハウ『プレースホルダとは何か?SQLインジェクション攻撃を回避せよ!
[2]@kamihorkOracleにおけるバインド変数の利用