「安全なSQLの呼び出し方」で学ぶSQLのセキュリティ

IPAは過去に届出を受けた脆弱性関連情報を基に、適切なセキュリティを考慮したウェブサイトを作成するための資料を公開しています。

https://www.ipa.go.jp/security/vuln/websecurity.html

その資料の中にある「安全なSQLの呼び出し方」を読み、SQL使用時のセキュリティ対策をまとめました。

SQLの構文

SQL文を構成する要素には、キーワード、演算子、識別子、リテラルなどがある。

SELECT name FROM user WHERE name='kona' and age>=20;
キーワード 演算子など 識別子 リテラル
SELECT = >= , name age ‘kona’ 20

リテラル

上記のSQL文に出てきた、‘kona’や20などの定数をリテラルと呼び、’kona‘は文字列リテラル、20は数値リテラルと呼ぶ。

SQLインジェクション

データベースを利用するアプリケーションの多くは、利用者からの入力をもとにSQL文を組み立てる。この組み立て方に問題があった場合、SQLインジェクションの脆弱性となる。その問題を悪用した攻撃をSQLインジェクションと呼ぶ。

以下はPerlによるSQL文作成の例

$q = "SELECT * FROM user WHERE id='$id'";

$idは外部から値を入力できるものとし、以下の値を入力した場合

';DELETE FROM user--

パラメータを展開したSQL文は以下のようになる。

SELECT * FROM user WHERE id='';DELETE FROM user--'

;で区切られ、DELETE FROM userが実行され、--以降はコメントとして無視される。これはSQLインジェクションの脆弱性といえる。

SQLの呼び出し方

文字列連結によるSQL文の組み立て

以下はPHPの例

$name = $_POST['name'];
//...
$sql = "SELECT * FROM employee WHERE name='" . $name . "'";

POSTメソッドで送信されたnameを変数に入れている。$nameが「kona」の場合以下のSQL文が生成される。

SELECT * FROM employee WHERE name='kona';

上記SQLの脆弱性をなくすためには$nameに対し、エスケープ処理を行う必要がある。

プレースホルダによるSQL文の組み立て

プレースホルダによる組み立ては、パラメータ部分を?などの記号で示しておき、後に値を割り当てる方法。 以下はJavaの例。

PreparedStatement prep = conn.prepareStatement("SELECT * FROM employee WHERE name=?");
prep.setString(1, "kona");

記号、?のことをプレースホルダと呼び、そこへ値を割り当てることをバインドするという。 バインドを行うタイミングによって2種類の組み立て方がある。

資料ではこの2種類を、静的プレースホルダ動的プレースホルダと呼んでいる。

静的プレースホルダ

プレースホルダのままのSQL文をデータベースに送信し、実行前にSQL文の構文解析などを準備しておく方式。 プレースホルダ前後のSQL文がすでに組み立てられているので、バインド時のエスケープも必要なく、原理上脆弱性が発生しない。

動的プレースホルダ

プレースホルダへ値をバインドする処理をアプリケーション側で行い、完成したSQL文をデータベースへ送信する方式。 静的プレースホルダに比べると、バインド処理を行うライブラリなどによって脆弱性が入り込む可能性もある。

まとめ

これまでのSQL文を組み立てる方法をまとめると、以下のように静的プレースホルダがもっとも安全性高くなる。

静的プレースホルダ > 動的プレースホルダ > 文字列連結による組み立て

sql