SQL インジェクション

SQL インジェクション(SQL Injection)は一般的なネットワーク攻撃手法であり、攻撃者は入力フィールドやリクエストに悪意のある SQL ステートメントを注入することで、データベースに意図しない操作を実行させます。

その目的は通常、次のとおりです:

  • 機密データの窃取
  • 認証のバイパス
  • データベース内容の改ざん、削除
  • システムコマンドの実行など

SQL インジェクションの動作原理

  • 入力検証の不足:Web アプリケーションがユーザー入力を適切に検証しない場合、攻撃者は入力フィールドに SQL コードを挿入できます。

  • SQL ステートメントの連結:アプリケーションのバックエンドは通常、ユーザー入力を SQL クエリと連結して、完全なデータベースクエリ文を形成します。

  • 悪意のある SQL の実行:アプリケーションが入力を適切にサニタイズまたはエスケープしない場合、悪意のある SQL コードがデータベースサーバーによって実行されます。

  • データ漏洩または破壊:攻撃者は SQL インジェクションを利用して、データベース内のデータを照会、改ざん、削除したり、データベース管理システムのシステムコマンドを実行したりできます。

正常なクエリ

ユーザーがウェブサイトにログインするとき、通常はユーザー名とパスワードを入力します。

以下は正常な SQL クエリコードです:

SELECT * FROM users WHERE username = 'user1' AND password = 'password1';

インジェクション攻撃

攻撃者が次のように入力した場合:

用户名: admin' --
密码: anything

SQL クエリは次のようになります:

SELECT * FROM users WHERE username = 'admin' --' AND password = 'anything';

このうち--「--」は SQL のコメント記号であり、パスワード条件を無視して、認証を直接バイパスします。


よくある SQL インジェクション

1、基本型 SQL インジェクション

悪意のある SQL コードをユーザー入力に直接埋め込み、クエリロジックに影響を与えます。

输入用户名:admin' OR '1'='1
输入密码:anything

実行される SQL クエリ:

SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything';

結果:

OR '1'='1' は常に true になるため、検証をバイパスできます。

2、UNION クエリインジェクション

UNION を使用して、攻撃者が作成したクエリ結果を正当なクエリ結果と結合し、機密データを取得します。

入力:

' UNION SELECT null, username, password FROM users --

実行される SQL クエリ:

SELECT id, name FROM products WHERE id = '' UNION SELECT null, username, password FROM users --';

結果:

users テーブルの username と password のデータが結果として返されます。

3、エラー型 SQL インジェクション

意図的にデータベースエラーを発生させ、エラー情報を利用してテーブル名、列名、またはデータを推測します。

入力:

' AND 1=CONVERT(int, (SELECT @@version)) --

実行される SQL クエリ:

SELECT * FROM users WHERE username = '' AND 1=CONVERT(int, (SELECT @@version)) --';

結果:

エラー情報によって、データベースのバージョンやその他の情報が漏洩する可能性があります。

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

クエリ結果を直接取得できない場合、攻撃者は返されたページの応答(ブール値や時間遅延など)を判断することで、データを段階的に推測します。

ブール型ブラインドインジェクション、入力:

' AND (SELECT 1 WHERE SUBSTRING((SELECT database()), 1, 1)='t') --

実行される SQL クエリ:

SELECT * FROM users WHERE username = '' AND (SELECT 1 WHERE SUBSTRING((SELECT database()), 1, 1)='t') --';

結果:

返された結果に基づいて、データベース名の最初の文字が t であるかどうかを判断します。

時間型ブラインドインジェクション、入力:

' AND IF(1=1, SLEEP(5), 0) --

実行される SQL クエリ:

SELECT * FROM users WHERE username = '' AND IF(1=1, SLEEP(5), 0) --';

結果:

条件が成立した場合、サーバーは 5 秒遅延して応答するため、情報が漏洩します。

5、スタッククエリインジェクション

複数の SQL ステートメントを同時に実行できます。

入力:

'; DROP TABLE users; --

実行される SQL クエリ:

SELECT * FROM users WHERE username = ''; DROP TABLE users; --';

結果:

users テーブルが削除されます。

一部のデータベース(MySQL など)は、デフォルトでは複数ステートメントの実行をサポートしていません。

6、ストアドプロシージャインジェクション

ストアドプロシージャの入力パラメータを利用して、悪意のある SQL を注入します。

入力:

'; EXEC xp_cmdshell('dir'); --

実行される SQL:

EXEC LoginProcedure 'username', ''; EXEC xp_cmdshell('dir'); --'

結果:

システムコマンドを実行します(例:ディレクトリの一覧表示)。

7、Cookie インジェクション

ブラウザに保存されている Cookie 値を変更して注入を行います。

Cookie: session_id=' OR '1'='1;

サーバーは Cookie を解析する際に、悪意のある SQL を実行します。


SQL インジェクションの危険性

  • データ漏洩:攻撃者はデータベース内のユーザー名、パスワード、銀行カード番号などの機密情報を取得します。
  • 権限昇格:攻撃者はインジェクションコマンドによって、より高いアクセス権限を取得する可能性があります。
  • データ改ざん:データベースの内容が変更または削除されます。
  • サービス中断:悪意のある SQL コードによってデータベースがクラッシュし、システムの可用性に影響を与える可能性があります。
  • システムコマンドの実行:データベースの拡張機能を通じて、攻撃者はオペレーティングシステムを直接操作できる可能性があります。

防御策

1、パラメータ化クエリとプリペアドステートメント

パラメータ化クエリまたはプリペアドステートメントを使用して、ユーザー入力を SQL ステートメントから分離し、ユーザー入力が SQL コードとして直接解析されることを防ぎます。

Java コード:

例

String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();

Node.js(MySQL モジュール):

例

const query = "SELECT * FROM users WHERE username = ? AND password = ?";
connection.query(query, [username, password], (err, results) => {
    if (err) throw err;
    // Handle results
});

2、ORM フレームワークの使用

核心となる考え方 ORM(Hibernate、Sequelize など)は SQL クエリを自動生成することで、手動での SQL 連結の機会を大幅に減らし、インジェクションを防ぎます。

// 使用 Sequelize
const user = await User.findOne({
    where: { username: 'admin', password: 'password123' }
});

3、入力検証

ユーザー入力が期待どおりであるかを厳密にチェックし、ルールに適合しない入力を拒否します。

ユーザー名やメールアドレスなどには正規表現を使用し、数値型フィールドでは数字のみを許可します。

特殊文字をエスケープします(例:" を \" に変換)。

const username = req.body.username.replace(/[^a-zA-Z0-9]/g, ''); // 清理特殊字符

4、データベース権限の制限

データベースユーザーに最小限の権限を割り当て、必要な操作のみを許可します。

  • 書き込み権限の制限:対応するテーブルへの INSERT と UPDATE のみを許可し、DROP、ALTER などの高リスクな操作は許可しません。
  • 読み取り/書き込み権限の分離:読み取り専用アカウントを使用してデータベースにアクセスします。

読み取り専用ユーザーを作成します:

CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'secure_password';
GRANT SELECT ON database_name.* TO 'readonly_user'@'%';

5、定期的なセキュリティテスト

セキュリティスキャンツールまたは手動テストを通じて、コード内の潜在的な SQL インジェクションの脆弱性を定期的にチェックします。

オープンソースツールの SQLMap を使用してテストできます。

SQLMap は、SQL インジェクションの検出と悪用を自動化するために特化したペネトレーションテストツールです。

SQLMap はネットワークセキュリティ評価やペネトレーションテストで広く使用されており、SQL インジェクションの脆弱性の発見と修正に役立ちます。

その他の拡張