【问题标题】:Django: SQL Injection - Use subquery in raw queryDjango:SQL 注入 - 在原始查询中使用子查询
【发布时间】:2018-09-26 10:58:36
【问题描述】:

当我尝试使用 Django recommendsparams 参数作为子查询将未执行的查询字符串传递到原始查询时遇到错误:

from apps.bikeshare.models import Station
qs = Station.objects.filter(...)

subquery_string = qs.values('id').order_by().query
raw = Station.objects.raw('SELECT * FROM bikeshare_station WHERE id IN (%s)', [subquery_string])

查询打印出正确的 SQL(WHERE 子句省略):

<RawQuerySet: SELECT * FROM bikeshare_station WHERE id IN (SELECT "bikeshare_station"."id" FROM "bikeshare_station" WHERE ...)>

但是,执行原始查询 (raw[0]) 会出错:

Traceback (most recent call last):
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/IPython/core/interactiveshell.py", line 2847, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-4-6eada3e31b4e>", line 1, in <module>
    raw[0]
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/models/query.py", line 1329, in __getitem__
    return list(self)[k]
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/models/query.py", line 1299, in __iter__
    query = iter(self.query)
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/models/sql/query.py", line 93, in __iter__
    self._execute_query()
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/models/sql/query.py", line 127, in _execute_query
    self.cursor.execute(self.sql, params)
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/gbrown/Envs/bikeshare-dev/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: can't adapt type 'Query'

我知道这很危险,因为它是 SQL 注入。但是,我想支持这一点,以创建一个 QuerySet 方法(恰好需要原始查询),该方法可用于查询集链的末尾,其中过滤的查询集作为子查询传递到原始查询中。

即使没有发生错误,params 参数是否会降低任何风险?还是我应该允许风险并使用基本的python字符串格式?

【问题讨论】:

  • 有什么理由不能在某处使用query_set.filter(id__in=your_list_of_ids)
  • @JonClements 谢谢,刚刚意识到我还有另一个问题,请参阅我的回答。

标签: python django


【解决方案1】:

我刚刚发布的方法无论如何都不能正常工作,因为query 方法没有返回有效的 SQL,即

qs = Station.objects.filter(name='Albert Gate, Hyde Park').values('id').order_by()
print(qs.query)

[out] SELECT "bikeshare_station"."id" FROM "bikeshare_station" WHERE "bikeshare_station"."name" = Albert Gate, Hyde Park

因为名称字符串没有被引用而爆炸。

似乎唯一的方法是,正如Jon Clements 在他的评论中指出的那样,执行查询并传入可以实现的 id 列表:

qs = Station.objects.filter(name='Albert Gate, Hyde Park')
ids = list(qs.values_list('id', flat=True))

然后您可以将 id 列表而不是字符串传递给原始查询。

【讨论】:

  • 出于好奇 - 我仍然不确定您在这里要做什么?查询 ID 子集中的名称,或获取与名称匹配的 ID 列表,然后从中获取原始 ORM 生成的 SQL 或...?只是想知道.raw 的东西是从哪里来的……
  • @JonClements 看到我的answer 到另一个问题。我试图终止以原始查询结尾的查询集链。我想将查询“按原样”作为原始查询中的子查询传递,以提供最大的灵活性。但是,由于query 返回的 SQL 无效,我无论如何也不能依赖它。
猜你喜欢
  • 2017-05-15
  • 2013-09-09
  • 2021-12-26
  • 2021-12-05
  • 2017-01-15
  • 1970-01-01
  • 2021-07-27
  • 2018-11-04
  • 2014-03-06
相关资源
最近更新 更多