FlawOpen

flawopen.com/SQL Injection/エスケープだけで十分?

引用符のエスケープでSQL Injectionを防げる?

CWE-89: SQL InjectionReference page
要点

通常は防げません。エスケープ関数はDB、接続文字セット、SQLモード、引用符の文脈に依存します。パラメータ化クエリはドライバーAPIを通じて値をSQL構文から分離します。値にはパラメータを使い、束縛できない動的識別子はアプリケーション内の固定許可リストから選びます。

実運用でエスケープが壊れやすい理由

接続文字セットはエスケープに影響する

エスケープは文脈に依存しない文字置換ではありません。MySQLの説明では、mysql_real_escape_string()は接続文字セットを使い、SET NAMESは関数が参照するクライアントライブラリの状態を更新しません。設定の不一致は、エスケープ済み文字列への前提を崩すことがあります。パラメータ化APIを優先し、旧コードでエスケープが必要なら、対応するドライバー関数で文字セットを設定し、エラーを処理します。

一度の呼び出し漏れで防御が崩れる

すべての補間箇所で、正しいドライバー、文字セット、SQLモード、文字列リテラルの文脈に合わせてエスケープする必要があります。パラメータ化APIは呼び出し時に値とデータを明示的に分離し、引用文字列を手作業で組み立てる必要をなくします。

引用符のエスケープはすべてのSQL文脈を守らない

引用符のエスケープが扱うのは特定の文字列リテラル内の文字だけです。テーブル名、列名、キーワード、並び順を安全に生成できず、引用符のない数値式への入力も守れません。値は束縛し、動的なSQL構造はアプリケーションの固定選択肢に対応させます。

VULNERABLE
// ユーザー入力をSQL構文に埋め込む
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
HARDENED
// ドライバーがnameをデータ値として束縛
cursor.execute(
    "SELECT id FROM users WHERE name = ?",
    (name,),
)

パラメータ束縛で何が変わるか

パラメータ化クエリはDB APIを通じてSQL構造と値を別々に渡します。ドライバーはネイティブのプリペアドステートメント、または別の安全な実装を使う場合があるため、特定のプロトコル手順を前提にしないでください。重要なのは値がデータとして扱われ、SQL構文として再解析されないことです。Python DB-APIではドライバーのプレースホルダーを使い、値を別引数で渡します。文字列フォーマットでクエリを作らないでください。

エンジニア向けチェックリスト

よくある質問

エスケープが適切な場合はある?

束縛できない識別子やSQL断片には、アプリケーションの固定許可リストと組み合わせて、ドライバーがサポートする引用APIだけを使います。任意の値に汎用エスケープを使わないでください。

ドライバーのエスケープ関数が保守されていれば安全?

文書化された文脈では役立ちますが、DB、接続設定、引用文脈、すべての呼び出し箇所での正しい使用に依存します。値にはパラメータ化クエリがより安全な標準です。

参考資料

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com ORM を使えば SQL インジェクションを防げるか Knex.jsはSQL Injectionを防げますか? MyBatisの${}と#{}:SQL Injectionを防ぐ MySQLiとPDO:安全なPrepared Statement NHibernate は SQL Injection を防げますか? MongoDB NoSQL Injection:安全なクエリフィルター