ナルミヤの備忘録(仮)

ナルミヤが学んだことなどを書き記していくブログ(方向性模索中。)

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インジェクション対策は以下のよう

  • プレースホルダーにによってSQL文の組み立てる
  • アプリケーション側でSQL文を組み立てる際に、リテラルを正しく構成するなど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 文字リテラルに対する対処

プレースホルダー?を使う。

参照