SQLインジェクションの練習

サーバ

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を狙ったプロンプトインジェクションにシフトすると思われます。