1.SQLインジェクションとは
過去問では、SQLインジェクションに関して、以下のように述べられている。
SQLインジェクションに関して(H22AP秋午前問43より) |
---|
「Webアプリケーションに悪意ある入力データを与えてデータベースの問合せや操作を行う命令文を組み立てて、データを改ざんしたり不正に情報取得したりする攻撃」 |
SQLインジェクションは、SQL文を操作する文字列をWebフォームなどに注入(Injection)し、データベースに不正にアクセスする攻撃。
例えば、以下のようなフォームがある。
サーバの認証プログラムとして、以下のSQL文が入っているとします。
SELECT user_id,passwd FROM users WHERE user_id='$user_id' AND passwd='$passwd';
ここで、フォームに以下を入力する
1' or '1' = '1'; -- (最後は半角スペース)
SQL文は以下になる。
SELECT user_id,passwd FROM users WHERE user_id='1' or '1' = '1'; -- ' AND passwd='$passwd';
コメントを意味する「--」を付ければ、それ以降が無視されます。結果、どんなユーザを入れても成立してしまいます。
コマンドを成功させるだけでなく、SQLのUNION句を使って別のSQL文をくっつければ、DBの中身を表示することも可能だ。
2.SQLインジェクションの対策
2.1 対策の全体像
SQLインジェクションの対策に関しては、IPAのサイトから以下が公開されています。
https://www.ipa.go.jp/security/vuln/websecurity/sql.html
根本的解決 |
---|
1-(i)-a SQL文の組み立ては全てプレースホルダで実装する。 SQLには通常、プレースホルダを用いてSQL文を組み立てる仕組みがあります。SQL文の雛形の中に変数の場所を示す記号(プレースホルダ)を置いて、後に、そこに実際の値を機械的な処理で割り当てるものです。ウェブアプリケーションで直接、文字列連結処理によってSQL文を組み立てる方法に比べて、プレースホルダでは、機械的な処理でSQL文が組み立てられるので、SQLインジェクションの脆弱性を解消できます。 プレースホルダに実際の値を割り当てる処理をバインドと呼びます。バインドの方式には、プレースホルダのままSQL文をコンパイルしておき、データベースエンジン側で値を割り当てる方式(静的プレースホルダ)と、アプリケーション側のデータベース接続ライブラリ内で値をエスケープ処理してプレースホルダにはめ込む方式(動的プレースホルダ)があります。静的プレースホルダは、SQLのISO/JIS規格では、準備された文(Prepared Statement)と呼ばれます。 どちらを用いてもSQLインジェクション脆弱性を解消できますが、原理的にSQLインジェクション脆弱性の可能性がなくなるという点で、静的プレースホルダの方が優ります。詳しくは本書別冊の「安全なSQLの呼び出し方」のプレースホルダの項(3.2節)を参照してください。 1-(i)-b SQL文の組み立てを文字列連結により行う場合は、エスケープ処理等を行うデータベースエンジンのAPIを用いて、SQL文のリテラルを正しく構成する。 SQL文の組み立てを文字列連結により行う場合は、SQL文中で可変となる値をリテラル(定数)の形で埋め込みます。値を文字列型として埋め込む場合は、値をシングルクォートで囲んで記述しますが、その際に文字列リテラル内で特別な意味を持つ記号文字をエスケープ処理します(たとえば、「'」→「''」、「\」→「\\」等)。値を数値型として埋め込む場合は、数値リテラルであることを確実にする処理(数値型へのキャスト等)を行います。 こうした処理で具体的に何をすべきかは、データベースエンジンの種類や設定によって異なるため、それにあわせた実装が必要です。データベースエンジンによっては、リテラルを文字列として生成する専用のAPI(*4)を提供しているものがありますので、それを利用することをお勧めします。詳しくは、「安全なSQLの呼び出し方」の4.1節を参照してください。 なお、この処理は、外部からの入力の影響を受ける値のみに限定して行うのではなく、SQL文を構成する全てのリテラル生成に対して行うべきです。 1-(ii) ウェブアプリケーションに渡されるパラメータにSQL文を直接指定しない。 これは、いわば「論外」の実装ですが、hiddenパラメータ等にSQL文をそのまま指定するという事例の届出がありましたので、避けるべき実装として紹介します。ウェブアプリケーションに渡されるパラメータにSQL文を直接指定する実装は、そのパラメータ値の改変により、データベースの不正利用につながる可能性があります。 |
保険的対策 |
---|
1-(iii) エラーメッセージをそのままブラウザに表示しない。 エラーメッセージの内容に、データベースの種類やエラーの原因、実行エラーを起こしたSQL文等の情報が含まれる場合、これらはSQLインジェクション攻撃につながる有用な情報となりえます。また、エラーメッセージは、攻撃の手がかりを与えるだけでなく、実際に攻撃された結果を表示する情報源として悪用される場合があります。データベースに関連するエラーメッセージは、利用者のブラウザ上に表示させないことをお勧めします。 1-(iv) データベースアカウントに適切な権限を与える。 |
ウェブアプリケーションがデータベースに接続する際に使用するアカウントの権限が必要以上に高い場合、攻撃による被害が深刻化する恐れがあります。ウェブアプリケーションからデータベースに渡す命令文を洗い出し、その命令文の実行に必要な最小限の権限をデータベースアカウントに与えてください。|
整理すると、こんな感じ
#### (1)根本的解決
〔1〕エスケープ処理
①バインド機構の利用
②プログラムでのエスケープ処理
入力フォームによる入口でのチェックも有用
#### (2)保険的対策
〔1〕エラーメッセージを非表示
エラーメッセージは、攻撃者に情報を与えてしまう。
また、ブラインドSQLインジェクション(Blind SQL injection)という手法があり、入力値と応答結果から、脆弱性を把握する。たとえば、ID/Passを入力したときに、「IDが存在しません」「パスワードが間違っています」と丁寧にエラーを返せば、攻撃者にもIDが違うのかPassが違うのかの情報を与えてしまう。
〔2〕データベースアカウント
権限の見直し。必要最小限の権限しか与えておかなければ、被害も少なくなる。
過去問をみてみましょう。
過去問(H21SC春午前Ⅱ問14) |
---|
SQLインジェクション対策について、Webアプリケーションの実装における対策とWebアプリケーションの実装以外の対策の組合せとして、適切なものはどれか。 |
↓
↓
↓
↓
↓
今回の内容はとても難しい内容ですが、SQLインジェクションというキーワードから、「データベース」に関する対策を選ぶと、ウのみになります。
2.2 静的プレースホルダ(バインド機構)
これを使えば、エスケープ処理は不要だ。
http://www.ipa.go.jp/files/000024396.pdf
静的プレースホルダは情報処理技術者試験で問われるキーワードです。バインド機構や、プリペアドステートメントという言葉を使う場合もあります。Prepared Statementなので「用意された構文」です。用意された構文が変化することなく、入力された文字は文字列として理解されるので不正な文字を入れるという攻撃を防げます。
過去問(H18秋SU午後Ⅱ問1)では、「バインド機構を利用する」ことの解説として、「プレースホルダと呼ばれる一時的な特殊文字を使用してSQL文のひな形を用意しておき,後で実際の値(変数)を割り当ててSQL文を完成させる方法です。変数は自動的にエスケープ処理されるので,DBMSの種類によって異なるエスケープ処理を意識する必要がなくなります」とあります。
具体例をみましょう。以下は、上記サイトの引用であるが、入力されたデータは「?」で表わされます。そして、入力されたデータが文字として直接実行されるのだ。
具体例 |
---|
$sth = $dbh->prepare( "SELECT id, name, tel, address, mail FROM usr WHERE uid=? AND passwd=?"); $sth->execute($uid, $passwd); |
わかったような…
これまでは、上記のuidのところに、以下のように入力されると、構文が変わってしまう。
1' or 'a'='a' --
しかし、?で処理し、値だけを判断するので、 「1' or 'a'='a' --」という文字列で処理される。
2.3 サニタイジング(エスケープ)処理
(1)サニタイジング(エスケープ)処理とは
SQL インジェクション対策はサニタイジング(エスケープ)処理が一般的です。
過去問では、SQLインジェクション攻撃を防ぐ方法として、「入力値から,データベースへの問合せや操作において特別な意味をもつ文字を解釈されないように保護する。(H20SU 午前問題 問26より)」と述べられている。
また、過去問(H18秋SU午後Ⅱ問1)では、「SQLインジェクション対策のためのエスケープ処理とは,利用者から入力される値が、SQL文にとって特別な意味をもつ記号文字の場合に,例えば,"'"であれば"''"のように"'"を二つ続けた文字列に置換する処理のことです。」とあります。
Webフォームなどに、<script> のようなスクリプトが埋め込まれた場合、HTMLではスクリプトと判断してスクリプトが実行されます。単純な例を挙げます。以下の記載をしたとします。
<script>alert("こんにちは");</script>
すると、画面に以下のようなポップアップ画面(アラート)が表示されます。あなたが掲示板の管理者だとして、このような書き込みをされたとすると、その掲示板を開くたびにこのアラートが出てしまいます。困りますよね。
掲示板でのポップアップレベルならいいですが、個人情報を抜かれたり、サーバを乗っ取るようなコマンドを仕掛けられることもあります。これは防がなければいけません。
(2)サニタイジング(エスケープ)処理の方法1
対策(エスケープ処理)として、<script>などの文字を、Webサーバ上ではプログラム用の言葉ではなく、単なる文字として認識させます。具体的には「<」を「<」としてエスケープ処理をします。すると、Webサーバ側では、<script> となりますので、これはプログラム用の言葉ではないと判断されるのです。
<script> 書いたとしても、
ブラウザ上では <script> と表示されるのですね。
はいそうです。ブラウザがきちんと自動で認識をしてくれます。掲示板などに<script>と書き込んで試してみるといいでしょう。
エスケープ処理(サニタイジング処理)における、文字の変換例は以下です。
表2 文字の置換
元の文字 | 置換後の文字列 |
---|---|
& | & |
< | < |
> | > |
' | ' |
" | " |
(H18年SV午後Ⅰ問1表2より引用)
これを丸暗記しようとすると難しいが、意味を理解すると覚えやすい。例えば「&」であれば、本来の名称であるampersand、「<」であれば、less than(~より小さい)のltである。
セキュアプログラミングに関しては、IPAのサイトに充実した解説がされている。[http://www.ipa.go.jp/security/awareness/vendor/programming/index.html](http://www.ipa.go.jp/security/awareness/vendor/programming/index.html)
(3)サニタイジング(エスケープ)処理の方法2
別の例を見ましょう。SQLインジェクション対策としてのエスケープ処理の具体的な処理としては、 ' を '' に変換します。サニタイジングをしない場合、UserNameに a' or '1'='1' と入力すると、データベース側では、
SELECT * FROM USER WHERE UserName = 'a' or '1'='1'
と判断され、'1'='1'は正しいことから、不正なアクセスが可能だ。
一方で、サニタイジングをすると、同じ入力をした場合、以下になる。
SELECT * FROM USER WHERE UserName = 'a'' or ''1''=''1'
こうなると、SQLの構文として不適切であり、この部分は「文字列」として扱われる。
なおかつ、データベース側では、''は'という文字列として自動認識してくれるので、入力した通りの文字として正常に処理される。
サニタイジングとエスケープ処理との違いはなんですか?
同じですか?
まあ、一緒と思ってもいいだろう。
サニタイジング(無害化)の方法の一つが文字の変換などによるエスケープ処理。サニタイジングの方法は、エスケープ処理以外に、特殊文字は入力させない、処理を強制終了させるなどがある。
又は、もっと単純に、’などの特殊文字をそもそも受け付けず、入力フォームにてエラーを出すことも方法の一つ。
◆参考:Taintモード
Perlの場合、Taintモード(汚染検出用のモード)がある。
IPAの以下の資料には、次のような効果が記載されている。http://www.ipa.go.jp/security/awareness/vendor/programmingv1/pdf/a04_03.pdf
IPA資料 |
---|
このモードでは,フォームからのデータのみでなく,コマンドライン引数,環境変数,ロカール情報 ,幾つかのシステムコール(readdir,readlink,getpw* 呼び出しのgecos フィールド)の結果,すべてのファイル入力などを汚染データとして扱い,これらのデータをサブシェルを起動するコマンド(system,exec など)や,ファイルやディレクトリ,プロセスに変更を加えるようなコマンド(unlink,umask など)の引数として使用した場合,エラーとしてくれる。 |
設定は簡単
\#!/usr/bin/perl -T ←これをつけるだけ
ただ、万能ではないし、実装されていることも少ない(気がする)。