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

Posted

技术标签:

【中文标题】避免 SQL 注入:范围内的原始 SQL【英文标题】:Avoid SQL Injection: Raw SQL in scope 【发布时间】: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) 已在代码的其他几个地方使用。因此,为它们创建了单独的范围。 【参考方案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 值的输入通过这种方式得到了适当的清理。

【讨论】:

以上是关于避免 SQL 注入:范围内的原始 SQL的主要内容,如果未能解决你的问题,请参考以下文章

避免mysql注入应该避免都有哪些特殊字符

MongoDB如何避免SQL注入混乱?

如何动态准备 SQL 查询(也包括列名)避免 SQL 注入

我可以通过使用参数来避免所有 SQL 注入攻击吗?

1021上课演练----SQL注入与避免(银行系统)

dljd_013_使用PreparedStatement避免SQL注入攻击