arel本身不防sql注入,安全取决于是否参数化;用arel::sql()、to_sql或动态字段名拼接均危险,仅user.arel_table[:col].eq(val)等参数化路径安全。

直接用 Arel 表达式拼接字符串就是注入入口
Arel 本身不是“更安全的 SQL 构建器”,它只是 ActiveRecord 的底层查询抽象层;Arel::Nodes::SqlLiteral、Arel.sql() 或手动调用 to_sql 后再插进 where,都会绕过参数化机制。你写的 Arel::Nodes::InfixOperation.new('=', user_col, Arel.sql("'#{params[:value]}'")) 和 "name = '#{params[:name]}'" 在数据库眼里完全等价。
哪些 Arel 操作是安全的?只用参数化路径
真正防注入的不是“用了 Arel”,而是是否把用户输入作为独立参数传入——Arel 支持问号占位符和命名绑定,但必须走 ActiveRecord 的执行链,不能提前求值。
-
User.where(User.arel_table[:email].eq(params[:email]))✅ 安全:Arel 自动转为参数化 WHERE 子句 -
User.where(User.arel_table[:status].in(params[:statuses]))✅ 安全:数组自动展开为(?, ?, ?)占位符 -
User.where(User.arel_table[:created_at].gteq(params[:since]))✅ 安全:时间/数字类型直通绑定 -
User.where(Arel.sql("email = '#{params[:email]}'"))❌ 危险:Arel.sql声明“这段我来负责”,ActiveRecord 不再干预 -
User.where(User.arel_table[:email].eq(params[:email]).to_sql)❌ 危险:to_sql返回纯字符串,已失去绑定上下文
动态字段名(如排序、搜索列)不能靠 Arel 绕过白名单
Arel 对字段名不做任何校验。写 User.order(User.arel_table[params[:sort]].asc) 看似用了 Arel,但 params[:sort] 若是 "email; DROP TABLE users; --",最终生成的 SQL 就是 ORDER BY email; DROP TABLE users; -- ASC。
- 必须先校验字段名是否在
User.column_names或预设白名单中 - 不要用
params[:sort]直接索引Arel::Table,应先过滤:safe_sort = User.column_names.include?(params[:sort]) ? params[:sort] : "id" - 方向同理:
params[:dir].in?(%w[asc desc]) ? params[:dir] : "asc"
find_by_sql 和 connection.execute 与 Arel 无关,但常被误认为“能用 Arel 包一层就安全”
有人试图这样“加固”:sql = User.arel_table.project(Arel.star).where(User.arel_table[:email].eq(params[:email])).to_sql; User.find_by_sql(sql)——这毫无意义。一旦走到 to_sql,字符串已生成;find_by_sql 接收的就是裸 SQL,不再识别任何绑定逻辑。
-
find_by_sql、connection.execute、select_all这些方法永远只接受字符串,不接受 Arel 对象 - 若必须用原生 SQL,唯一安全路径是:仅用
?、:named占位符,且所有用户输入只作为参数传入,不参与字符串拼接 - 例如:
ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE status = ?", params[:status])✅;ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE status = '#{params[:status]}'")❌
Arel 不是防护层,它是构建工具;它不拦截、不校验、不转义——它只忠实翻译你给它的结构。真正的防护点始终只有一个:用户输入是否以参数形式进入数据库驱动,而不是以字符串片段混进 SQL 文本里。这点在 Rails 7 里没变,也不会变。











