SQLインジェクション攻撃とは?
SQLインジェクション攻撃とは、Webアプリケーションのデータベース操作部分の脆弱性を悪用して、悪意のあるSQL文を実行させる攻撃のことです。
この攻撃が成功すると、データベース内の全ての情報が盗まれる可能性があり、2023年のセキュリティ調査でも依然として最も危険な脆弱性の一つとして挙げられています。
特にWebアプリケーション開発者にとっては、避けては通れない重要なセキュリティ課題となっています。
SQLインジェクションの仕組み
基本的な攻撃の流れ
SQLインジェクションがどのように動作するかを、具体例を使って説明します。
例えば、以下のような脆弱なログイン処理があるとします:
<?php
// 危険なコード例(絶対に使わないでください)
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($connection, $sql);
?>
この場合、通常のログインでは以下のようなSQL文が生成されます:
SELECT * FROM users WHERE username = 'tanaka' AND password = 'mypassword123'
攻撃者の悪用方法
しかし、攻撃者がユーザー名欄に以下のような文字列を入力したらどうでしょうか?
' OR '1'='1' --
この場合、生成されるSQL文は以下のようになります:
SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = 'anything'
--
はSQLのコメント文なので、AND password = 'anything'
の部分は無視されます。
そして'1'='1'
は常に真(true)なので、結果的に全てのユーザー情報が取得されてしまいます。
具体的な攻撃例
1. ログイン回避攻撃
先ほどの例のように、認証を回避して不正にログインする攻撃です。
攻撃パターン例:
ユーザー名: admin' --
パスワード: (何でも良い)
2. 情報窃取攻撃(UNION攻撃)
UNION文を使って、本来表示されないデータを取得する攻撃です。
-- 攻撃者が入力する値
' UNION SELECT username, password FROM admin_users --
これにより、管理者のユーザー名とパスワードが画面に表示されてしまう可能性があります。
3. データベース破壊攻撃
さらに悪質なケースでは、データベースの内容を削除する攻撃も可能です。
-- 攻撃者が入力する値
'; DROP TABLE users; --
この場合、ユーザー情報が保存されているテーブル全体が削除されてしまいます。
SQLインジェクションの対策方法
1. プリペアドステートメント(最重要)
最も効果的な対策は、プリペアドステートメント(パラメータ化クエリ)を使用することです。
<?php
// 安全なコード例
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$user = $stmt->fetch();
?>
Node.js(Express + MySQL)の例:
// 安全なコード例
const mysql = require('mysql2/promise');
const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
const [rows] = await connection.execute(query, [username, password]);
Python(Django)の例:
# 安全なコード例
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", [username, password])
user = cursor.fetchone()
2. 入力値の検証とサニタイゼーション
ユーザーからの入力値を適切に検証し、危険な文字をエスケープします。
<?php
// 入力値のサニタイゼーション
$username = mysqli_real_escape_string($connection, $_POST['username']);
$password = mysqli_real_escape_string($connection, $_POST['password']);
// さらに入力値の形式チェック
if (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
die('無効なユーザー名です');
}
?>
3. 最小権限の原則
データベースユーザーには必要最小限の権限のみを付与します。
-- 良い例:アプリケーション用ユーザーの作成
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'strong_password';
-- 必要な権限のみ付与(DROP、CREATE等の権限は付与しない)
GRANT SELECT, INSERT, UPDATE ON myapp.users TO 'app_user'@'localhost';
GRANT SELECT, INSERT, UPDATE ON myapp.orders TO 'app_user'@'localhost';
4. WAF(Web Application Firewall)の活用
WAFを導入することで、SQLインジェクションの攻撃パターンを自動的に検出・ブロックできます。
主要なWAFサービス:
- CloudFlare
- AWS WAF
- Azure Application Gateway
5. データベースの設定強化
-- 危険なSQL文の実行を制限
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO';
-- 複数文の実行を無効化
SET global general_log = 'OFF';
フレームワーク別の対策
Laravel(PHP)
// Laravelでの安全な実装
$users = DB::table('users')
->where('username', $username)
->where('password', $password)
->get();
// または Eloquent ORM を使用
$user = User::where('username', $username)
->where('password', $password)
->first();
Express.js(Node.js)
// Sequelize ORM を使用した安全な実装
const user = await User.findOne({
where: {
username: username,
password: password
}
});
Django(Python)
# Django ORMを使用した安全な実装
from django.contrib.auth.models import User
user = User.objects.filter(username=username, password=password).first()
脆弱性の発見方法
1. 手動テスト
以下のような文字列を入力フィールドに入力してテストします:
' OR '1'='1' --
' OR '1'='1' /*
' OR '1'='1' #
' UNION SELECT 1,2,3 --
2. 自動化ツールの活用
OWASP ZAPやBurp Suiteなどのツールを使用して、自動的に脆弱性を検出できます。
実際の被害事例
事例1:大手ECサイトの情報流出
2023年に発生した某大手ECサイトの事例では、SQLインジェクションにより約50万件の顧客情報が流出しました。
被害の内容:
- 顧客の氏名、住所、電話番号
- メールアドレス
- 暗号化されていないパスワード
- クレジットカード情報の一部
事例2:金融機関のシステム侵入
海外の金融機関では、SQLインジェクションを起点とした攻撃により、数億円相当の不正送金が発生しました。
これらの事例から分かるように、SQLインジェクションは単なる情報漏洩にとどまらず、深刻な金銭被害を引き起こす可能性があります。
開発者が今すぐできること
1. 既存コードの見直し
既存のアプリケーションで、以下のような危険なパターンがないか確認してください:
// 危険なパターン
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
$sql = "SELECT * FROM products WHERE name LIKE '%" . $_POST['search'] . "%'";
2. セキュアコーディングの習慣化
新しいコードを書く際は、必ずプリペアドステートメントを使用しましょう。
3. 定期的なセキュリティテスト
開発サイクルにセキュリティテストを組み込み、定期的に脆弱性をチェックしましょう。
まとめ
SQLインジェクションは、Web開発者にとって最も重要なセキュリティリスクの一つです。
重要なポイント:
- プリペアドステートメントを使用する(最重要)
- 入力値の検証とサニタイゼーションを徹底する
- データベースユーザーには最小権限のみを付与する
- WAFを活用して多層防御を構築する
- 定期的なセキュリティテストを実施する
これらの対策を適切に実施することで、SQLインジェクション攻撃からアプリケーションを守ることができます。
セキュリティは「後から追加する」ものではなく、「最初から組み込む」ものです。今日から実践して、安全なWebアプリケーションを開発しましょう。
コメント