概要 #
今回の実験レポートは、「SQLインジェクション(SQLi)」の脆弱性に関する実証と対策の検証です。
ローカル環境(Windows上のXAMPP)に意図的に脆弱なWebアプリケーション(PHP/MySQL)を構築し、SQLインジェクション攻撃をシミュレートします。
最後に、これらの攻撃がなぜ成立したのか、そして根本的な対策(プレースホルダ)について考察します。# 実験内容
実験は以下のステップで行います。
- **1. 認証回避:**SQLの論理を操作し、パスワードなしでログインします。
- 2. 情報取得:
UNION演算子を悪用し、データベース内の全情報を盗み出します。 - 3. ファイル表示:
LOAD\_FILE()関数を悪用し、サーバー上のローカルファイルを読み取ります。 - **4. データベース改ざん:**スタッククエリを悪用し、データベースの値を不正に書き換えます。
1. 環境構築 #
- OS: Windows 11
- サーバー: XAMPP (Apache, MariaDB(MySQL))
- 脆弱なアプリケーション: 以下のPHPスクリプトを自作し、
test\_dbデータベースとusersテーブルを準備しました。
# データベースへアクセス
PS C:\xampp\mysql\bin> .\mysql.exe -u root
# test_dbデータベースを作成
MariaDB [(none)]> CREATE DATABASE test_db;
# users テーブルを作成
MariaDB [(none)]> USE test_db;
MariaDB [test_db]> CREATE TABLE users ( -> id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, -> username VARCHAR(50) NOT NULL, -> password VARCHAR(50) NOT NULL -> );
# users テーブルの構造を確認
MariaDB [test_db]> DESC users;
# admin と user を登録
MariaDB [test_db]> INSERT INTO users (username, password) VALUES -> ('admin', 'password123'), -> ('user', 'pass');
# 登録内容の最終確認
MariaDB [test_db]> SELECT * FROM users;
+----+----------+-------------+
| id | username | password |
+----+----------+-------------+
| 1 | admin | password123 |
| 2 | user | pass |
+----+----------+-------------+
2. 実験1:認証回避 #
**目的:**パスワードを知らなくても、WHERE 句の論理を操作して常に「真」にすることで、ログイン認証を突破する。
使用ファイル #
index.html(ログインフォーム)
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ログインフォーム(脆弱な例)</title>
</head>
<body>
<h2>ログイン</h2>
<form action="spli1.php" method="POST">
<div>
<label for="username">ユーザー名:</label>
<input type="text" id="username" name="username">
</div>
<div>
<label for="password">パスワード:</label>
<input type="password" id="password" name="password">
</div>
<button type="submit">ログイン</button>
</form>
</body>
</html>

実行しようとしたクエリ:
"; echo "$sql"; $result = $conn->query($sql); if ($result && $result->num_rows > 0) { echo "
ログイン成功 #
"; echo "ようこそ、" . htmlspecialchars($user, ENT_QUOTES, 'UTF-8') . " さん
"; } else { echo "ログイン失敗 #
"; echo "ユーザー名またはパスワードが間違っています。
"; } $conn->close(); ?>
## 実行ペイロード
ユーザー名とパスワード入力欄に以下の文字列を入力します。
ユーザー名:admin パスワード:' OR '1'='1
## 実行されるSQL
PHPによって組み立てられた結果、SQLは以下のようになります。
SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1'
## 結果と解説
MySQLは `AND` よりも `OR` の優先度が低いため、このSQLは `(username = 'admin' AND password = '')` OR `('1'='1')` と解釈されます。
前半が偽 (False) であっても、後半の `'1'='1'` が常に真 (True) であるため、`WHERE` 句全体が真となります。結果として、`spli1.php` の `$result->num\_rows > 0` の条件を満たし、「ログイン成功」と表示されました。
<div class="zoomable-image-container"><img src="/images/posts/experiments/post05/1.png" alt="img1"></div>---
# 3. 実験2:データベースの情報取得とファイル表示
**目的:**`UNION` 演算子と `LOAD\_FILE()` 関数を使い、データベース内の情報やサーバー上のファイルを取得する。
## 使用ファイル
- `index.html`
COMMENTS 3