SQLインジェクションについて
ば先メモをこちらの方で。
学習内容が進むたびに随時追加予定(必ず追加するとは言ってない)。
1. SQLインジェクションの原因
1.1 SQLクエリの構成
例
SELECT a,b,c FROM atable WHERE name='YAMADA' and age>=20
この文の中には、
がある。
1.2 リテラルとは
がある
1.3 SQLインジェクションに対する脆弱性の原因
安全なSQLの呼び出し方曰く、
SQL 文の文字列リテラルをパラメータ化しているときに、そこに別の SQL 文の断片を含ませることで、元の SQL 文の意味を変更できる場合がある。これが SQL インジェクションの脆弱性である
1.4 文字列リテラルに対するSQLインジェクション
例えば、SQLクエリにPHPの変数$idを使って以下のようにSQLを呼び出す時、
$q = "SELECT * FROM atable WHERE id='$id'";
この時、$idを以下のようにしてみる。
';DELETE FROM atable--
そうすると、実行されるクエリは、
SELECT * FROM atable WHERE id='';DELETE FROM atable--'
となり、SELECT 文の後ろに DELETE 文が追加され、データベースの内容がすべて削除される結果となる。(SQLでは--以降はコメントとして無視される)
1.5 数値リテラルに対するSQLインジェクション
先ほどと同様に、
$q = "SELECT * FROM atable WHERE id=$id";
とした時、
0;DELETE FROM atable
とすると、
SELECT * FROM atable WHERE id=0;DELETE FROM atable
となり、テーブルからデータが消える。
1.6 SQLインジェクションの例
- エラーメッセージによる漏洩(リテラルにcastを入れる)
- UNION SELECTによる漏洩(リテラルにUNION SELECTを入れる)
SQLインジェクションによる認証回避
$id = $_POST['id']; $pwd = $_POST['pwd']; $sql = "SELECT * FROM users WHERE id='$id' and pwd='$pwd'";
において、
$_POST['pwd'] = ' or 'a' ='a
とすると、クエリは、
SELECT * FROM users WHERE id='[入力した任意のID]' and pwd='' or 'a' ='a'
となり、WHERE句が常に成立するためにログインできてしまう。
SQLインジェクション攻撃によるデータ改ざん(リテラルにupdate文を入れる)
1.7 その他SQLインジェクション攻撃
- OSコマンドの実行
- ファイルの読み出し
- ファイルの書き出し
- HTTPリクエストにより他のサーバーを攻撃
- データベース中の表名・列名の調査方法も...
2. 対策
2.1 対策の基本
対策の基本としては以下の2点をクリアしてるべき。
そのために、「体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践」によると、SQLインジェクション対策は以下のよう
後者は全ての場合を対策するのが難しいので前者の対策を強くおすすめするらしい
2.2 プレースホルダーについて
プレースホルダーには以下の2種類ある。
以下、引用
2.2.1 静的プレースホルダー
静的プレースホルダは、JIS/ISO の規格では「準備された文(Prepared Statement)」と規定されている。これは、プレースホルダのままの SQL 文をデータベースエンジン側にあらかじめ送信して、実行前に、SQL 文の構文解析などの準備をしておく方式である。 セキュリティの観点で、最も安全である。静的プレースホルダでは、SQL を準備する段階で SQL 文の構文が確定し、後から SQL 構文が変化することがないため、パラメータの値がリテラルの外にはみ出す現象が起きない。その結果として、SQL インジェクションの脆弱性は生じない。
2.2.2 動的プレースホルダー
動的プレースホルダは準備された文(Prepared Statement)とは異なり、プレースホルダを利用するものの、パラメータのバインド処理をデータベースエンジン側で行うのではなく、アプリケーション側のライブラリ内で実行する方式である。 セキュリティの観点では、プレースホルダを用いたバインド処理によってパラメータの値の埋め込みがライブラリで機械的に処理されることから、文字列連結による組み立てに比べてアプリケーション開発者のミスによるエスケープ漏れを防止できると期待される。 ただし、動的プレースホルダは静的プレースホルダとは異なり、バインド処理を実現するライブラリによっては、SQL 構文を変化させるような SQL インジェクションを許してしまう、脆弱な実装のものが存在する可能性は否定できない。
2.3 実際の対応
PHPの言語を用いている。
2.3.1 数値リテラルに対する対処
入力されたものが数字か否かでインジェクションを防ぐ。
なお、POSTやGETで得られた値は、str型なので単に、gettype($_POST['userid'])=="integer"
としてもfalseになることに注意。
$id = $_POST['userid']; if (preg_match("/^[0-9]+$/",$id)) { $_SESSION['userId'] = $id; } else { header("Location:http://localhost/lessons/vendingMachine/vendingHome.php?id_err=1"); exit(); }
リダイレクト先でisset($_GET['id_err'])ならIDエラーであると表示するならびに、そもそも、その際はSQL文を実行しないことでSQLインジェクションを回避する。
2.3.2 文字リテラルに対する対処
プレースホルダー?を使う。