Table 列上带有参数的 Django 原始查询(SQL 注入)

Django Raw Query with params on Table Column (SQL Injection)

我有一个不寻常的场景,但除了我的 sql 参数之外,我还需要让用户 / API 定义 table 列名称。 params 我的问题是查询结果为:

SELECT device_id, time, 's0' ...

而不是

SELECT device_id, time, s0 ...

还有其他方法可以通过 raw 实现吗?还是我需要自己转义该列?

queryset = Measurement.objects.raw(
            '''
            SELECT device_id, time, %(sensor)s FROM measurements
            WHERE device_id=%(device_id)s AND time >= to_timestamp(%(start)s) AND time <= to_timestamp(%(end)s)
            ORDER BY time ASC;
            ''', {'device_id': device_id, 'sensor': sensor, 'start': start, 'end': end})

您发布的示例查询似乎不需要 raw()。我认为以下查询集非常相似。

measurements = Measurement.objects.filter(
    device_id=device_id, 
    to_timestamp__gte=start,
    to_timestamp__lte,
).order_by('time')

for measurement in measurements:
    print(getattr(measurement, sensor)

如果您需要优化并避免加载其他字段,您可以使用values()only()

对于任何可能的 SQL 注入,要小心

但从本质上讲,这是一个相当普遍的问题,并且有一个相当安全的解决方案。一般来说,问题是查询参数 "the right way" 用于处理查询值,但它们不是为架构元素设计的。

要在查询中动态包含架构元素,通常必须求助于字符串连接。这正是我们都被告知不要处理 SQL 查询的事情。

但这里的好消息是您不必使用实际的用户输入。这是因为,虽然可能的查询值是无限的,但可能的 有效模式元素 的超集是非常有限的。因此,您可以根据该超集验证用户的输入。

例如,考虑以下过程:

  1. 用户输入一个值作为列名。
  2. 代码将该值(原始字符串比较)与已知可能值列表进行比较。 (这个列表可以是硬编码的,也可以从数据库模式中动态获取。)
  3. 如果找不到匹配项,return 出错。
  4. 如果找到匹配项,则直接在 SQL 查询中使用匹配的已知值。

因此,您所使用的只是您作为程序员放入代码中的字符串。无论如何,这与自己编写 SQL 相同。