【セキュリティ】SQLインジェクションを実験してみる
今回は、IPA(情報処理推進機構)の「安全なウェブサイトの作り方」のはじめに登場するSQLインジェクションについて、実際にWEBサイトを構築して実験してみたいと思います。
1. DBの作成
まずは、Webページからアクセス可能なデータベースsectestを作成します。
今回はMariaDBを使用しています。
MariaDB [(none)]> create database if not exists sectest; //DB sectestを作成
Query OK, 1 row affected (0.000 sec)
MariaDB [(none)]> use sectest; //DB sectestを選択
Database changed
MariaDB [sectest]> create table users (
-> id int auto_increment primary key,
-> username varchar(100),
-> password varchar(100)
-> ); //usersテーブルを作成
Query OK, 0 rows affected (0.016 sec)
MariaDB [sectest]> insert into users (username, password) values
-> ('admin', 'adminpassword'),
-> ('haji', 'hajipassword'),
-> ('papa', 'papapassword'); //usersテーブルにレコードを挿入
Query OK, 3 rows affected (0.003 sec)
Records: 3 Duplicates: 0 Warnings: 0
2. DBにアクセスするユーザーを作成
MariaDBにユーザーsectestを作成し、データベースsectestを操作する権限を与えます。
MariaDB [sectest]> create user 'sectest'@'localhost' identified by 'sectestpass'; //sectestというユーザーを作成
Query OK, 0 rows affected (0.008 sec)
MariaDB [sectest]> grant select, insert, update, delete on sectest.* to 'sectest'@'localhost'; //sectestユーザーに、sectestテーブルへのselect, insert, update ,delete権限を付与
Query OK, 0 rows affected (0.008 sec)
MariaDB [sectest]> flush privileges; //設定した権限を適用
Query OK, 0 rows affected (0.000 sec)
3. Webサイトの作成
脆弱性のあるWebページを作成します。WebサーバーはApacheを使っています。
下記のsectest.htmlとsectest.phpは、/var/www/htmlディレクトリに保存します。
sectest.html
<html>
<head>
<title>SQL injection test</title>
</head>
<body>
<h2>ユーザー情報</h2>
<form method="GET" action="sectest.php">
ユーザー名: <input type="text" name="username">
<input type="submit" value="検索">
</form>
</body>
</html>

sectest.php
<?php
$servername = "localhost";
$username = "sectest"; // DB sectestにアクセスするユーザー
$password = "sectestpass";
$dbname = "sectest";
$conn = new mysqli($servername, $username, $password, $dbname);
// エラー確認
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error); // 接続に失敗したら、エラー内容を表示してスクリプト停止
}
$user = $_GET['username']; // localhost/sectest.php?username=xxx という形式でusernameが渡されるので、$userには`xxx`の部分が格納される。
$sql = "SELECT * FROM users WHERE username = '$user'"; // **ユーザーが入力した値$userを直接SQL文に入れているので、ここがSQLインジェクションの原因となる**
$result = $conn->query($sql);
echo "<h2>クエリ: $sql</h2>";
if ($result && $result->num_rows > 0) {
echo "<h3>ユーザー情報:</h3><ul>";
while($row = $result->fetch_assoc()) {
echo "<li>ID: " . $row["id"] . ", USER: " . $row["username"] . ", PASS: " . $row["password"] . "</li>";
}
echo "</ul>";
} else {
echo "一致するユーザーは見つかりませんでした。";
}
$conn->close();
?>
4. SQLインジェクションの実験

sectest.htmlのユーザー名に『haji』と入れて検索ボタンを押すと、
localhost/sectest/sectest.php?username=haji
というURLでsectest.phpが呼び出されます。
下図がその結果です。
SQLクエリの実行結果が表示されていることがわかります。

では、SQLインジェクション攻撃をしてみましょう。
ユーザー名に『1' or '1'='1』と入れて検索ボタンを押します。

WHERE句の後の「or '1'='1'」のところで、WHERE句の結果が必ずTrueになりますので、usersテーブルの全レコードが表示されてしまいます。

これにより、ユーザーIDとパスワード情報が漏洩してしまいました。
これが起こった原因は、PHPスクリプトの「$sql = "SELECT * FROM users WHERE username = '$user'";」の部分で、ユーザーが入力した文字列をそのまま$userの部分に代入してしまっていることが原因です。
次回は、ここからOSコマンドインジェクションに繋げていきたいと思います。