【问题标题】:Avoid SQL Injection: Raw SQL in scope避免 SQL 注入:范围内的原始 SQL
【发布时间】:2021-07-26 02:16:09
【问题描述】:

我的模型中有以下范围:

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 注入并获得正确的输出吗?

【问题讨论】:

  • 很简单:为了避免 SQL 注入,不要注入。在您的查询中完全使用#{...} 是无效的。始终绑定为占位符。
  • 这是计算列名吗?您是否有某种必须计算列名的模式?如果是这样,那就是关系数据库故障。代替一堆列,创建一个关系表,你可以JOIN。这使您的查询变得微不足道。
  • 它不起作用,因为占位符用于值而不是标识符(列名,表名,...)或 SQL 表达式;您的占位符可能最终会被单引号字符串替换。那么AgreementInstallment.calculated_status_sql(date)AgreementInstallment.calculated_amount_remaining_or_paid(date) 返回什么?
  • 这两个实际上都是案例陈述。 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 '未来' END } 结束
  • AgreementInstallment.calculated_status_sql(date) 和 AgreementInstallment.calculated_amount_remaining_or_paid(date) 已在代码的其他几个地方使用。因此,为它们创建了单独的范围。

标签: ruby-on-rails postgresql sql-injection


【解决方案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 '未来' END } 结束

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

您可以在下面构建您的子查询

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 值的输入通过这种方式得到了适当的清理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-02
    • 1970-01-01
    • 2023-03-25
    • 2018-10-14
    • 2014-06-16
    • 2011-01-13
    相关资源
    最近更新 更多