FlawOpen

flawopen.com/Rust の SQL インジェクション/sqlx

sqlx のコンパイル時クエリ検査は SQL インジェクションを防ぎますか?

リファレンスページ
要点

query! はコンパイル時に実際のデータベーススキーマとリテラルクエリを検証します。動的に組み立てたクエリが後から安全になるわけではありません。バインドパラメーターで値と SQL を分離します。

危険 — 入力を SQL に埋め込む
let user_id: String = read_user_input();
let q = format!(
  "SELECT id FROM users WHERE id = '{}'",
  user_id
);
// Representative only: data and SQL syntax are mixed.
// Never pass user-influenced SQL as AssertSqlSafe.
安全 — query! で値をバインド
let user_id: &str = read_user_input();
// PostgreSQL; query! checks the configured schema.
sqlx::query!(
  "SELECT id FROM users WHERE id = $1",
  user_id
).fetch_one(&pool).await?

コンパイル時検査が実際に検証するもの

SQLx 0.9 の query! マクロは文字列リテラルを要求し、ビルド時に設定済みのデータベーススキーマに照らして構文、バインド値の型、結果の型を検証します。DATABASE_URL を設定するか、管理対象のオフラインメタデータを使います。このコード例は PostgreSQL 専用で、プレースホルダーは $1 です。SQLx は PostgreSQL と SQLite では $1、MySQL と MariaDB では位置指定の ? を推奨しています。SQL 構造が固定またはアプリケーション側で管理されていれば、実行時の query() でも .bind() で値をバインドできます。SQLx 0.9 では動的なクエリ文字列を AssertSqlSafe 経由で渡しますが、これは安全性を表明する型であり、文字列をサニタイズしません。信頼できない値を補間した文字列に対して決して使用しないでください。

コードベースの確認方法

rg -n 'format!\s*\(|sqlx::query(_as)?\s*\(' --glob '*.rs'

よくある質問

常に query() より query! マクロを使うべきですか?

リテラルクエリとビルド時スキーマの要件がプロジェクトに合うなら query! を優先してください。SQL と型のコンパイル時検査が追加されます。動的な query() もすべての値を .bind() で渡せば安全です。どちらの場合も、信頼できないデータを SQL 構文へ埋め込まないでください。

参考資料

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