SQLインジェクションの動きを説明する為に、あえて脆弱性を残したログインページを作りました。
VM環境で簡単に実験できるようにサンプルソースを載せておきます。
かなり脆弱なHPですので、外部公開しないようにしてください。
環境
サーバ VMware Ubuntu 22.04
Webサーバ Apache
準備
次のプログラムを配置します。
/var/www/html パーミッション 777
sqli_01.php パーミッション 755
sqlite_config.php パーミッション 755 (単体実行できないようにしています)
/var/www/db パーミッション 777
sqli.db パーミッション 777 (sqlite3で作成したDBファイル)
ソースコード
//filename sqli_01.php
<?PHP
// 直接呼出しを禁止する
if (!defined('ALLOW_ACCESS')) {
die('このファイルは直接読み込むことができません。');
}
// DBコネクション
const DB_PATH = 'sqlite:/var/www/db/sqli.db'; // データベースのPATH
function getDbConnection(){
try {
$conn = new PDO(DB_PATH);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "データベースに接続できません: " . $e->getMessage();
exit();
}
return $conn;
}
// meta header page************************************************************************
//HTMLの一番最初に呼び出すヘッダー情報
function meta_header($this_program){
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset = "utf-8" >
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SQLインジェクションテストページ</title>
</head>
<body>
<a href="<?php echo "$this_program"?>">reload</a><br>
<?PHP
}
// footer page**************************************************************************
function meta_footer($this_program){
?>
<hr>
</body>
</html>
<?php
}
function requestCheck($param){
if(isset($_GET[$param]) || isset($_POST[$param])){
return true;
} else {
return false;
}
}
function getRequestParam($param) {
if(isset( $_POST[ $param ] ) && $_POST[ $param ]!= null ) {
return $_POST[$param];
} else if(isset($_GET[$param]) && $_GET[ $param ]!= null ) {
return $_GET[$param];
} else {
return "";
}
}
?>
// sqlite_config.php 単体実行不可
<?PHP
// 直接呼出しを禁止する
if (!defined('ALLOW_ACCESS')) {
die('このファイルは直接読み込むことができません。');
}
// DBコネクション
const DB_PATH = 'sqlite:/var/www/html/db/sqli.db'; // データベースのPATH
function getDbConnection(){
try {
$conn = new PDO(DB_PATH);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "データベースに接続できません: " . $e->getMessage();
exit();
}
return $conn;
}
// meta header page************************************************************************
//HTMLの一番最初に呼び出すヘッダー情報
function meta_header(){
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset = "utf-8" >
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SQLインジェクションテストページ</title>
</head>
<body>
<a href="<?php echo "$this_program"?>">reload</a><br>
<?PHP
}
// footer page**************************************************************************
function meta_footer($this_program){
?>
<hr>
</body>
</html>
<?php
}
function requestCheck($param){
if(isset($_GET[$param]) || isset($_POST[$param])){
return true;
} else {
return false;
}
}
function getRequestParam($param) {
if(isset( $_POST[ $param ] ) && $_POST[ $param ]!= null ) {
return $_POST[$param];
} else if(isset($_GET[$param]) && $_GET[ $param ]!= null ) {
return $_GET[$param];
} else {
return "";
}
}
?>
dbファイル
sqlite3を使って次のコマンドを入力して作成します。
作成したdbファイルは /var/www/db/sqli.db に保存しました。
フォルダとDBファイルのパーミッションを777 にします。
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL
);
テスト用ユーザの作成
INSERT INTO USERS(username, password) VALUES ('TAROU', '1234');
実行の様子
クライアントから
http://(VMのIPアドレス)/sqli_01.php
にアクセスすると、
このような感じになります。
適当なIDとパスワードを入力すると実行されたSQLが表示されます。
SQLインジェクションを用いるとログインすることができるのですが、エクスプロイトコードになるのでこのページには記載しません。
最後に
このプログラムをサーバの外部公開フォルダに置いて実験したところ、WAF(Web Aplication Firewall)によってブロックされました。(素晴らしい!)
私はSQLインジェクション自体、すでに過去のHack技術だと思っています。
しかし、いまだに被害が出ている手法ですので、脆弱性の残っているHPを根絶するため、プログラマ全体の質が向上することを願うばかりです。
今後はLLMを狙ったプロンプトインジェクションにシフトすると思われます。