SQLインジェクションのリスクと対策

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()

タグ: SQLインジェクション PyMySQL データベースセキュリティ SQLセキュリティ パラメータ化クエリ

7月16日 21:07 投稿