flawopen.com/Python の SQL Injection/SQLAlchemy
SQLAlchemy text() は SQL Injection に安全?
リファレンスガイド
要点
バインドパラメーターは、クエリ用紙に入れる封印済みの値カードのようなものです。SQLAlchemy は SQL 文とパラメーターの対応表を基盤の DBAPI に渡すため、アプリのコードが組み立てる SQL 式に値を混ぜずに済みます。データベース固有の束縛は DBAPI ドライバが処理するため、通信方式は異なる場合があります。text() は SQL テキストを受け取りますが、すでに補間された値をサニタイズしたりパラメーター化したりはしません。
危険 — 入力値を SQL に補間
stmt = text(
f"SELECT id FROM users WHERE id = {user_id}"
)
rows = conn.execute(stmt)
強化 — 値をバインドし SQL 構造を制限
stmt = text(
"SELECT id FROM users WHERE id = :id"
)
rows = conn.execute(stmt, {"id": user_id})
# Bind values, not identifiers. Choose any
# selectable column from a fixed application map.
columns = {"name": users.c.name,
"created": users.c.created_at}
column = columns.get(requested_sort, users.c.created_at)
stmt = select(users.c.id).order_by(column)
rows = conn.execute(stmt)
text() が行うことと行わないこと
text() はテキスト形式の SQL 構文を作りますが、入力を補間済みの文字列はサニタイズしません。SQL に値のプレースホルダーを宣言し、Connection.execute() または Session.execute() に値を別に渡します。SQLAlchemy Core の式 API は適切な場合に Python のリテラル値をバインドしますが、テキスト断片は SQL 構文です。パラメーターはテーブル名、列名、ソート方向の代わりにはなりません。ユーザーが選ぶ構造は固定の SQLAlchemy 式に対応付けます。パラメーター化は SQL Injection を防ぎますが、認可や業務ルールの検証を置き換えません。
コードベースの監査方法
rg -n 'text\(|\.execute\(' --glob '*.py' .よくある質問
SQLAlchemy Core と ORM のクエリ構築は常に安全ですか?
通常の式構築では Python の値をバインドしますが、安全性は文の構築方法によります。テキスト SQL、リテラル SQL 構文、動的識別子も確認してください。入力を連結した後に SQL 文字列を安全にする API はありません。