FlawOpen

flawopen.com/Rust 中的 SQL 注入/sqlx

sqlx 编译时查询检查能防止 SQL 注入吗?

参考页面
简要回答

query! 会在编译时根据真实数据库 schema 验证字面量查询;它不会让动态拼接的查询事后变安全。应使用绑定参数,让值与 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! 宏要求使用字符串字面量,并在构建时根据已配置的数据库架构检查 SQL 语法、绑定参数类型和结果类型;需要配置 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() 吗?

当项目满足字面量查询和构建时 schema 的要求时,优先使用 query!,它还会在编译时检查 SQL 和类型。动态 query() 只要对每个值调用 .bind() 也安全。无论哪种方式,都不要把不可信数据插入 SQL 语法。

参考资料

另请参阅 SQL 注入(CWE-89):原理与各编程语言的防护方法 — flawopen.com 存储过程能防止 SQL 注入吗? TypeORM QueryBuilder 能防止 SQL 注入吗? Django ORM 与 SQL Injection:安全写法 Doctrine ORM SQL 注入:安全查询实践 EF Core FromSqlRaw:安全参数化 SQL FastAPI SQL 注入:Pydantic 能防什么,不能防什么