この記事では、《SQLインジェクション》について、
業務を通して学習した内容を、備忘録としてまとめています。
- 『良いSQL』と『悪いSQL』
- 『SQLインジェクション』とは…?
こういった内容についてまとめています。
※本記事は、自分で学習したことのまとめ用として書いています。
尚、解説で誤った点があれば、スローして頂ければ喜んでキャッチしますのでお願い致します。
環境
- apache2.4
- php7
- MySQL5.7
『良いSQL』 と 『悪いSQL』
良いSQL
-
メンテナンスしやすい
自分が書いたSQL文を、他の人がメンテナンスすることもある -
パフォーマンスが良い
データ量、実行頻度、同時アクセスが増加しても、導入当初と同等のレスポンスが維持できる
悪いSQL
-
必要以上のデータをメモリに読み込む
パフォーマンスの劣化問題
セキュリティ上の問題 -
索引(index)を使用せず、全件検索する
パフォーマンスの劣化問題
(I/Oの増加、メモリの非効率的利用)
セキュリティの観点から見た 『悪いSQL』
ズバリ・・・
__『SQLインジェクション』の脆弱性があるSQL文__です。
『SQLインジェクション』 とは…?
『SQLインジェクション』とは・・・
データベースと連動したWebサイトで…
- データベースへの問合せ
- データベースへの操作
などを行うプログラムに、パラメータとしてSQL文の断片を与えることにより…
- データベースの改ざん
- データベースから不正に情報を入手
する攻撃です。
--- 『SQLインジェクション』 の例題 ---
例題として、__認証を回避する『SQLインジェクション』__があります。
SQL文は、よくログイン認証に使われますが、
例えば・・・
以下のような__フォームの入力値を代入した変数を利用して生成したSQL文__がtrue
であれば、__ログインに成功する__というモノがあります。

SELECT *
FROM
users
WHERE
user_name = '$user_name'
AND password = '$password';
この$password
に…
'or'x'='x
という不正な値を入力します。
SELECT *
FROM
users
WHERE
user_name = '$user_name'
AND password = ''or'x'='x';
これだと分かりづらいので、改行すると…
SELECT *
FROM
users
WHERE
user_name = '$user_name'
AND password = ''
OR 'x' = 'x';
上記のように、WHERE句にOR
の条件が追加されます。
すると・・・
OR 'x' = 'x'
がtrue
となるので、ログインに成功してしまいます。
上記は、SQL文の構造が変わってしまったため…
user_name = '$user_name'
AND password = ''
または・・・
OR 'x' = 'x'
が
true
になればログインできるSQL文に、変わってしまったということです。
『SQLインジェクション』の対策 【PHPの場合】
対策: 『プリペアドステートメント』を使う
『プリペアドステートメント』を使い、実行したいSQL文をコンパイルすることで・・・
SQL文を固定し、構造が変わらないようにします。
『プリペアドステートメントの使い方』について詳しく知りたい方は、
下記を参照してみて下さい。
-
プリペアドステートメント
SQL文で値がいつでも変更できるように、変更する箇所だけ変数のようにしたSQL文を作る仕組みのことです。
通常『プリペアドステートメント』は『プレースホルダ』を使うために作られます。 -
プレースホルダ
ユーザが入力した内容を後から挿入するために、予め確保した場所のこと。
仮に、パスワードに…
OR 'x' = 'x'
と入力しても、SQL文がコンパイルされているので、
OR 'x' = 'x'
というパスワードを探しにいく。
当然、そのようなパスワードはないため、ログインできない。
まとめ: 基本コーディング
<?php
try {
// リクエストから得たスーパーグローバル変数をチェックするなどの処理
$user_name = "ここにユーザー名が入ります";
$password = "ここにパスワードが入ります";
// データベース接続設定
$pdo = new PDO(
'mysql:host=サーバー名;dbname=DB名;charset=文字エンコード',
'ユーザー名',
'パスワード',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
// パラメータ
$spl = (
'SELECT
USER_NAME, PASSWORD
FROM
テーブル名
WHERE
AND USER_NAME = :USER_NAME
AND PASSWORD = :PASSWORD'
);
// プリペアドステートメントを用意
$stmt = $pdo->prepare($sql);
// 値をバインドする
$stmt->bindValue(':USER_NAME', $user_name, PDO::PARAMS_STR);
$stmt->bindValue(':PASSWORD', $password, PDO::PARAMS_STR);
// 結果を取得する
$row = $prepare->fetch();
} catch (PDOException $e) {
// エラー処理
}