はじめに
この度は本記事を閲覧いただき誠にありがとうございます。
今回はSQLインジェクションについて私が学習した内容をまとめて整理したいと思いこの記事を書くことになりました。表現がおかしいところもあると思いますが、ご了承くださいませ。
SQLインジェクションって?
SQLインジェクションとは、ユーザーの入力値をそのままSQL文に組み込んでしまうことで、攻撃者が意図しないSQL文を実行できてしまう脆弱性、およびそれを悪用する攻撃手法です。
脆弱性のあるコード
ログイン処理
// ログイン処理(脆弱な例)
$user = $_POST['user_id'];
$pass = $_POST['password'];
$sql = "SELECT * FROM users WHERE id = '" . $user . "' AND password = '" . $pass . "'";
$result = $conn->query($sql);
攻撃例
user_id の入力欄に以下のような文字列を入力します。
' OR '1'='1' --
(-- の後には半角スペースが必要です。MySQLでは -- の直後に空白や制御文字がないとコメントとして認識されません)
実際に組み立てられるSQL
SELECT * FROM users WHERE id = '' OR '1'='1' -- ' AND password = ''
-- 以降はすべてコメントとして無視されるため、実質的に実行されるのは次の部分だけになります。
SELECT * FROM users WHERE id = '' OR '1'='1'
'1'='1' は常に真になるため、パスワードの条件がまるごと消えてしまい、パスワードなしで全ユーザー分のレコードを取得=認証を突破できてしまいます。
安全なコード例(根本的解決:プレースホルダ)
$user = $_POST['user_id'];
$pass = $_POST['password'];
// プレースホルダ「?」を使い、値は後からバインドする
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND password = ?");
$stmt->execute([$user, $pass]);
$result = $stmt->fetchAll();
安全なコードとして、プレースホルダを使う方法があります。これはデータを挿入する前のSQL文の骨組みを先にDB側でコンパイルしておき、後から値だけを安全にバインドする仕組みです。こうすることで、データ入力部分にSQL文のような文字列が含まれていても、それは単なる「値」として扱われるため、SQL文として解釈・実行されることがなくなります。
最後に一言
SQLインジェクションを防ぐ一つの方法として、プレースホルダを解説しました。
初めてSQLインジェクションについて学びましたが、情報セキュリティって面白いなと思いました。自分がホワイトハッカーにでもなったような気分になれて、実際のWebアプリ開発でも意識して設計ができると思いました。