SQLインジェクションのリスク
- SQLインジェクションの定義
- SQLインジェクションのリスク確認
- ユーザーログインシミュレーション
- 通常のログイン:
SELECT * FROM userinfo WHERE name = 'カイガ318' AND pwd = 666;
- 注入攻撃:
SELECT * FROM userinfo WHERE name = 'カイガ318';-- AND pwd = 'パスワードです';
- アカウントを知らなくても攻撃可能:
SELECT * FROM userinfo WHERE name = 'アカウント不明' OR 1=1;-- AND password = 'パスワードです';
- SQLインジェクションのリスク回避方法
- PyMySQLを使用したSQLインジェクションの防止例
SQLインジェクションとは?
SQLインジェクションのリスク確認
-- ユーザーテーブルを作成
CREATE TABLE userinfo (id INT PRIMARY KEY AUTO_INCREMENT, name CHAR(12) UNIQUE NOT NULL, pwd INT NOT NULL);
-- テスト用のユーザーデータを挿入
INSERT INTO userinfo (name, pwd) VALUES ('カイガ318', 666);
-- テーブル構造
mysql> SELECT * FROM userinfo;
+------+-----------+------+
| id | name | pwd |
+------+-----------+------+
| 1 | カイガ318 | 666 |
+------+-----------+------+
1 row in set (0.00 sec)
-
ユーザーログインシミュレーション
-
通常のログイン: SELECT * FROM userinfo WHERE name = 'カイガ318' AND pwd = 666;
-
注入攻撃: SELECT * FROM userinfo WHERE name = 'カイガ318';-- AND pwd = 'パスワードです';
-
SQL言語では -- は以降の文をコメントアウトするため、アカウントを知っているだけで目的の結果を得られる。これがSQLインジェクションのリスク。
-
アカウントを知らない場合でも攻撃可能: SELECT * FROM userinfo WHERE name = 'アカウント不明' OR 1=1;-- AND password = 'パスワードです';
-
このような状況もSQLインジェクションのリスクである。
SQLインジェクションのリスク回避方法
1. ユーザーからの入力は常に信頼しない。正規表現や長さ制限などで入力を検証し、単一引用符やダブルハイフンを変換する。
2. 動的にSQLを組み立てるのではなく、パラメータ化されたSQLやストアドプロシージュを使用する。
3. 管理者権限のデータベース接続を使用しない。各アプリケーションに別々の制限付きのデータベース接続を使用する。
4. 機密情報は直接保存しない。パスワードや敏感な情報を暗号化またはハッシュ化する。
5. アプリケーションのエラーメッセージは最小限にする。独自のエラーメッセージで元のエラーメッセージを隠す。
6. SQLインジェクションの検出には専用ツールやプラットフォームを使用する。例えばjskyやMDCSOFT SCANなど。
7. Webアプリケーションのデータベース操作権限を厳しく制限する。
8. アプリケーションの公開前に専門的なSQLインジェクション検出ツールを使用して検査を行う。例えばsqlmapやSQLninjaなど。
9. すべてのクエリでパラメータ化されたクエリを使用する。database/sqlのPrepareやQuery、Exec関数を使用する。
PyMySQLを使用したSQLインジェクションの防止例
# 間違いの例: 直接入力をSQLに組み込む
import pymysql
conn = pymysql.connect(host='127.0.0.1', user='root', password='318', database='ftp')
cur = conn.cursor()
username = input('user >>> ')
password = input('passwd >>> ')
sql = "SELECT * FROM userinfo WHERE name = %s AND password = %s" % (username, password)
cur.execute(sql)
print(cur.fetchone())
cur.close()
conn.close()
# 正しい例: executeメソッドにパラメータを渡す
import pymysql
conn = pymysql.connect(host='127.0.0.1', user='root', password='318', database='ftp')
cur = conn.cursor()
username = input('user >>> ')
password = input('pwd >>> ')
sql = "SELECT * FROM userinfo WHERE name = %s AND pwd = %s"
cur.execute(sql, (username, password))
print(cur.fetchone())
cur.close()
conn.close()