避免 SQL 注入:范围内的原始 SQL

Avoid SQL Injection: Raw SQL in scope

提问人:farha 提问时间:5/4/2021 最后编辑:farha 更新时间:5/4/2021 访问量:240

问:

我的模型中有以下范围:

scope :with_total_status, -> (date = Date.today) do
    select("agreements.*, (
      SELECT json_agg(statuses)
      FROM (
        SELECT
          ? AS calculated_status,
          SUM(?) AS total
        FROM agreement_installments
        WHERE agreement_installments.agreement_id = agreements.id
        GROUP BY calculated_status
      ) AS statuses
    ) agreement_status", "#{ AgreementInstallment.calculated_status_sql(date) }", "#{ AgreementInstallment.calculated_amount_remaining_or_paid(date) }")
  end

为了避免SQL注入,我根据需要在两个地方使用了。它不起作用,给我任何输出。但是,以下操作可以正常工作:?

scope :with_total_status, -> (date = Date.today) do
    select("agreements.*, (
      SELECT json_agg(statuses)
      FROM (
        SELECT
          #{AgreementInstallment.calculated_status_sql(date)} AS calculated_status,
          SUM(#{AgreementInstallment.calculated_amount_remaining_or_paid(date)}) AS total
        FROM agreement_installments
        WHERE agreement_installments.agreement_id = agreements.id
        GROUP BY calculated_status
      ) AS statuses
    ) agreement_status")
  end

我真的不确定出了什么问题。你能给我正确的方向来避免SQL注入并获得正确的输出吗?

Ruby-on-Rails PostgreSQL SQL注入

评论

1赞 tadman 5/4/2021
这很简单:为了避免 SQL 注入,不要注入。这在您的查询中完全是无效的。始终绑定为占位符。#{...}
0赞 tadman 5/4/2021
这是在计算列名吗?您是否有某种架构,其中包含必须计算的列名?如果是这样,那就是关系数据库故障。与其做一堆列,不如做一个关系表。这使得您的查询变得微不足道。JOIN
1赞 mu is too short 5/4/2021
它不起作用,因为占位符用于值,而不是标识符(列名、表名等)或 SQL 表达式;您的占位符最终可能会被单引号字符串替换。那么,返回什么呢?AgreementInstallment.calculated_status_sql(date)AgreementInstallment.calculated_amount_remaining_or_paid(date)
0赞 farha 5/4/2021
这两者实际上都是案例陈述。def self.calculated_status_sql(date, agreement_id = “agreements.id”) %Q{ case when agreement_installments.amount = ( SELECT sum(amount) FROM agreement_payments WHERE agreement_payments.agreement_installment_id = agreement_installments.id ) THEN 'paid' WHEN —————— ELSE 'future' END } end
0赞 farha 5/4/2021
AgreementInstallment.calculated_status_sql(date) 和 AgreementInstallment.calculated_amount_remaining_or_paid(date) 已在代码的其他几个地方使用。因此,为这些创建了单独的范围。

答:

0赞 Marlin Pierce 5/4/2021 #1

这两者实际上都是案例陈述。def self.calculated_status_sql(date, agreement_id = “agreements.id”) %Q{ case when agreement_installments.amount = ( SELECT sum(amount) FROM agreement_payments WHERE agreement_payments.agreement_installment_id = agreement_installments.id ) THEN 'paid' WHEN —————— ELSE 'future' END } end

在 ActiveRecord 中,该方法不像该方法那样采用替换参数(可能称为绑定变量)。selectwhere

您可以在下面构建子查询

SELECT sum(amount) FROM agreement_payments
WHERE agreement_payments.agreement_installment_id = agreement_installments.id

使用 AgreementPayment 模型(如果有)作为

sum_query = AgreementPayment.select('sum(amount)').where(agreement_installment_id: agreement_id)

然后在您构建的外部 SQL 语句中使用。sum_query.to_sql

这样做可能最终仍会将您构建的 SQL 注入到最终 SQL 中,因此它有风险,您需要小心。但是,您不会获取原始用户输入并将其注入到最终的 SQL 语句中。以这种方式正确审查 id 值的输入。