flawopen.com/SQL Injection/エスケープだけで十分?
引用符のエスケープでSQL Injectionを防げる?
通常は防げません。エスケープ関数はDB、接続文字セット、SQLモード、引用符の文脈に依存します。パラメータ化クエリはドライバーAPIを通じて値をSQL構文から分離します。値にはパラメータを使い、束縛できない動的識別子はアプリケーション内の固定許可リストから選びます。
実運用でエスケープが壊れやすい理由
エスケープは文脈に依存しない文字置換ではありません。MySQLの説明では、mysql_real_escape_string()は接続文字セットを使い、SET NAMESは関数が参照するクライアントライブラリの状態を更新しません。設定の不一致は、エスケープ済み文字列への前提を崩すことがあります。パラメータ化APIを優先し、旧コードでエスケープが必要なら、対応するドライバー関数で文字セットを設定し、エラーを処理します。
すべての補間箇所で、正しいドライバー、文字セット、SQLモード、文字列リテラルの文脈に合わせてエスケープする必要があります。パラメータ化APIは呼び出し時に値とデータを明示的に分離し、引用文字列を手作業で組み立てる必要をなくします。
引用符のエスケープが扱うのは特定の文字列リテラル内の文字だけです。テーブル名、列名、キーワード、並び順を安全に生成できず、引用符のない数値式への入力も守れません。値は束縛し、動的なSQL構造はアプリケーションの固定選択肢に対応させます。
// ユーザー入力をSQL構文に埋め込む
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
// ドライバーがnameをデータ値として束縛
cursor.execute(
"SELECT id FROM users WHERE name = ?",
(name,),
)
パラメータ束縛で何が変わるか
パラメータ化クエリはDB APIを通じてSQL構造と値を別々に渡します。ドライバーはネイティブのプリペアドステートメント、または別の安全な実装を使う場合があるため、特定のプロトコル手順を前提にしないでください。重要なのは値がデータとして扱われ、SQL構文として再解析されないことです。Python DB-APIではドライバーのプレースホルダーを使い、値を別引数で渡します。文字列フォーマットでクエリを作らないでください。
エンジニア向けチェックリスト
- ✓ユーザー制御の値はすべてドライバーAPIで束縛し、SQL文字列に埋め込まない。
- ✓動的なテーブル名、列名、並び順はアプリケーション内の固定許可リストから選ぶ。
- ✓旧式エスケープをすぐに削除できない場合は、ドライバーの文脈対応APIを使い、接続文字セットとSQLモードを確認する。
- ✓破壊的な文を実行せず、引用符、非ASCII文字、無効な識別子でクエリ構築をテストする。
よくある質問
束縛できない識別子やSQL断片には、アプリケーションの固定許可リストと組み合わせて、ドライバーがサポートする引用APIだけを使います。任意の値に汎用エスケープを使わないでください。
文書化された文脈では役立ちますが、DB、接続設定、引用文脈、すべての呼び出し箇所での正しい使用に依存します。値にはパラメータ化クエリがより安全な標準です。