「データベースに権限をやりすぎない」という最強の防御術:SQLiを致命傷にしないための設計論
現場のエンジニア諸君、お疲れ様。今日もどこかで脆弱性が叩かれているかもしれないな。
さて、今日は「SQLインジェクション(SQLi)」の対策について話そう。教科書には「プレースホルダを使え」「入力値をサニタイズしろ」と書いてある。それは大前提として、その対策を突破された時の「最後の一線」をどう守るかについて、泥臭い話をしたい。
SQLiの真の恐怖は、アプリケーションがDBサーバーに対して「何でもできる権限」を持っていることにある。もし、Webアプリ用のDBユーザーが DROP TABLE や xp_cmdshell (SQL Serverの場合) を実行できたら? その瞬間、データは人質になり、バックアップも消し飛ぶ。
1. なぜ「権限最小化」が究極の防御なのか
攻撃者はSQLiの脆弱性を見つけると、まず UNION SELECT でテーブル構造を覗き、次に information_schema を漁り、最終的にはOSコマンドの実行やデータの全流出を試みる。
しかし、もしアプリケーションが「必要なテーブル以外触れない」「SELECTしかできない」「ストアドプロシージャしか実行できない」という状態だったらどうなるか。攻撃者がどんなに巧妙なSQLを送っても、データベース側が「お前にはその権限がない」と門前払いする。これが、防御の多層化だ。
2. 権限設計の実践:アプリケーションユーザーの「去勢」
WebアプリのDB接続ユーザーには、以下のルールを適用するのが鉄則だ。
- DDL(DROP/ALTER/CREATE)権限は絶対不可: Webアプリが実行時にテーブル構造を変える必要は、まずない。
- ストアドプロシージャ活用によるカプセル化: テーブルへの直接アクセスを禁じ、特定のストアドプロシージャ経由でのみ操作を許可する。これにより、SQL注入のベクトルを劇的に制限できる。
PostgreSQLでの権限付与例(SQL)
— 1. アプリ専用ロールを作成
CREATE ROLE web_app_user WITH LOGIN PASSWORD ‘strong_password’;
— 2. 特定のスキーマのみアクセスを許可
GRANT USAGE ON SCHEMA public TO web_app_user;
— 3. テーブルの直接参照を禁止し、プロシージャのみ許可
— アプリはテーブルを直接叩けない
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM web_app_user;
— 必要なプロシージャのみ実行権限を与える
GRANT EXECUTE ON FUNCTION get_user_profile(int) TO web_app_user;
3. 実装のキモ:Pythonでの安全なデータアクセス
SQLiを技術的に防ぐには、やはり「プレースホルダ(バインド変数)」が必須だ。だが、権限分離ができていれば、万が一バインドをミスしても「被害を最小限に抑える」ことができる。
Python (psycopg2) を用いたセキュアな実装例
import psycopg2
def get_user_profile(user_id):
# 接続先は必ず権限を制限されたユーザーで行う
conn = psycopg2.connect(“dbname=myapp user=web_app_user password=…”)
cur = conn.cursor()
# 悪い例: f”SELECT FROM users WHERE id = {user_id}”
# ↑絶対やるな。これがSQLiの入り口だ。
# 良い例: プレースホルダを使用する
# データベース側でクエリの構造が固定されるため、データがコマンドとして解釈されない
query = “SELECT get_user_profile(%s);”
cur.execute(query, (user_id,))
result = cur.fetchone()
cur.close()
conn.close()
return result
4. 攻撃者が諦める「見えない壁」を作る
最後に、インフラ側での防御も忘れてはならない。クラウド環境であれば、IAMやセキュリティグループでDBへのアクセス元をWebサーバーに限定するのは当然として、WAF(AWS WAF等)でSQLi特有のパターンをブロックしておく。
AWS WAFでのSQLi検出ルール(概念)
WAFは「最後の砦」だが、過信は禁物だ。あくまで「明らかな攻撃」を弾くフィルターとして使う。
- SQL injection match conditions:
UNION,SELECT,OR 1=1といったシグネチャを監視。 - 重要: WAFだけで守ろうとせず、バックエンドのデータベース権限が正しく設定されているかを確認する「定期的な監査スクリプト」をCI/CDに組み込むこと。
最後に:エンジニアへの提言
脆弱性を見つけるのは攻撃者の仕事だが、「攻撃されても被害を出さない」のは我々エンジニアの誇りだ。
「開発速度」を理由に root や db_owner 権限でアプリを動かすのは、今日で終わりにしよう。権限を絞れば、コードレビューで少しのミスがあっても、DB側が守ってくれる。
今日から君たちのプロジェクトのDBユーザー権限を確認してみてくれ。過剰な権限を見つけたら、それが「次期インシデント」の芽だ。今のうちに摘んでおこう。
また現場で会おう。質問があればいつでもどうぞ。
コメント