FlawOpen

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 はありません。

参考資料

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com sqlx のコンパイル時クエリ検査は SQL インジェクションを防ぎますか? ストアドプロシージャは SQL インジェクションを防ぎますか? TypeORM QueryBuilder は SQL Injection に安全? Django ORMとSQL Injection:安全な書き方 Doctrine ORMのSQLインジェクション対策 EF Core FromSqlRaw:安全なSQLパラメーター化