FlawOpen

flawopen.com/Ruby 中的 SQL Injection/ActiveRecord where()

Rails where() 字符串插值安全吗?

参考页面
简答

把 SQL 想成一张说明固定、留有空格填写数值的表单。绑定参数只填写空格;Ruby 插值会在 Rails 区分两者之前,把请求文本写进 SQL 指令。保持 SQL 结构固定,并将不可信值作为参数传入。

UNSAFE
User.where(
  "id = #{params[:id]}"
)
SAFE
User.where(id: params[:id])
# or, for more complex conditions:
User.where("id = ?", params[:id])

为什么插值会改变查询

Active Record 收到条件之前,Ruby 就会先计算 #{...}。例如,where("id = #{params[:id]}") 会把已经拼接好的 SQL 字符串交给 Rails;请求输入可能因此改变谓词。Active Record 文档明确指出,这种字符串拼接方式不安全。支持的相等、范围或子集条件可使用哈希;其他谓词可用 ? 占位符。占位符绑定的是值,不能绑定标识符或 SQL 关键字;应固定查询结构,或从严格的允许列表中选择标识符。

如何检查代码

rg -n 'where\s*\(' app/
检查匹配结果,查找条件字符串中插值或拼接的请求数据。Brakeman 在分析识别出不安全数据流时会报告 SQL Injection 风险;扫描无告警并不能证明所有查询都安全。

常见问题

所有查询都能使用哈希条件吗?

不能。Rails 哈希条件支持相等、范围和子集(IN)检查。其他谓词应使用结构固定且值已绑定的 SQL 条件,例如 where("price > ?", amount)。绑定可以保护值,但不能让动态表名、列名或 SQL 片段变安全。

参考资料

另请参阅 SQL 注入(CWE-89):原理与各编程语言的防护方法 — flawopen.com Sequelize query() 能防止 SQL 注入吗? SQLAlchemy text() 能防止 SQL 注入吗? sqlx 编译时查询检查能防止 SQL 注入吗? 存储过程能防止 SQL 注入吗? TypeORM QueryBuilder 能防止 SQL 注入吗? Django ORM 与 SQL Injection:安全写法