保护 select 语句免受 sql 注入

Protect select statement from sql injection

我最近偶然发现了 api 的一部分,用户可以在其中查询特定字段:

api/models?only=id,name

是这样实现的:

@model = Model.select params[:only]

我对这一行有一种不祥的预感,我尝试了是否可以将 sql 注入到这部分代码中……并且成功了。

经过深思熟虑,我们想到了这个

@model = Model.select params[:only].split(',').map(&:to_sym)

这实际上阻止了注入。但这足以保护 API 的这一部分吗?

在这种情况下可以使用强参数吗?

query_params.require(:only).permit(:id,:name)

不起作用,因为传递的字段是字符串并且没有方法 permit

您可以使用ActiveRecord::Base.sanitize

Model.select Model.sanitize(params[:only])

实际上,控制器不应该使用模型的实现细节。 SQL 的 select 中的值是用逗号分隔的,而你的 API 按原样接受它们的事实是 "coincidence" 一开始不应该依赖的地方。

我将向您介绍您可能会觉得有用的工具,以便编写您的解决方案。

# Array#&, set intersection
[1, 2, 3] & [2, 4, 1] # => [1, 2]

# Model.attributes
Model.attributes #=> ['id', 'name', ...]
# You can filter down the resulting array to produce a whitelist of columns

# Splat the array into the argument list
Model.select(*params[:only].split(',') & Model::SELECT_WHITELIST)
# ...the constant above has to be defined inside the model, obviously.