TL; DR:Func()
Django文档中的
所有示例都可以轻松地用于通过一个参数安全地实现其他类似的SQL函数。所有后代的内置Django数据库功能和条件函数
Func()在设计上也都是安全的。超出此限制的应用程序需要评论。
Func() 类是Django Query表达式中最通用的部分。它允许以某种方式在Django ORM中实现几乎所有函数或运算符。它
就像一把瑞士军刀
,非常通用,但是与专用工具(例如带有光学屏障的电动刀具)相比,切勿割伤自己一定要多加注意。如果“升级的”“安全”小刀一旦不能放入口袋,则用铁锤用锤子锻造自己的工具仍然更加安全。
安全须知
有关
Func(*expressions, **extra)
示例的简短文档应首先阅读。(我在这里建议针对Django 2.0的开发文档,最近在其中添加了更多安全信息,包括避免与示例完全相关的SQL注入。)在所有的位置参数
*expressions
都 编译 通过Django的,那就是Value(string)
移动到的参数,在那里它们被正确地数据库驱动程序逃脱。其他字符串被解释为字段名称
F(name)
,然后以右table_name.
别名点作为前缀,最终添加到该表的联接,并且名称由quote_name()
函数处理。- 问题在于1.11中的文档仍然很简单,诱人的参数
**extra
和**extra_context
文档模糊地记录了下来。它们只能用于 永远不会“编译” 并且永远不会通过SQL的简单参数params
。带有安全字符但不带撇号,反斜杠或百分数的数字或简单字符串都很好。它不能是字段名,因为它不会明确,也不会联接。对于以前检查的数字和固定字符串(例如“ ASC” /“ DESC”),时区名称和其他值(例如,从下拉列表中),这是安全的。仍然有一个弱点。下拉列表值必须在服务器端检查。另外,还必须验证数字是否为数字,而不是数字字符串,例如'2'
因为所有数据库函数都默默接受省略的数字字符串而不是数字。如果传递了错误的“数字”,'0) from my_app.my_table; rogue_sql; --'
则注入结束。请注意,在这种情况下,流氓字符串不包含任何非常禁止的字符。用户提供的数字必须经过专门检查,或者值必须通过position传递expressions
。 - 它是安全的指定
function
名称和arg_joiner
函数功能类的字符串的属性或相同function
和arg_joiner
Func键()调用的参数。该template
参数绝不能在括号内的替换参数表达式周围包含撇号:(
%(expressions)s
)
,因为如果需要,数据库驱动程序会添加撇号,但是附加的撇号可能会导致其通常无法正常工作,但有时可能会被忽略,从而导致另一个安全问题。
与安全无关的注释
- 许多带有一个参数的简单内置函数看起来并不那么简单,因为它们是从Func的多用途后代派生的。例如
Length
,一个函数也可以用作查找Transform
。class Length(Transform):"""Return the number of characters in the expression."""function = 'LENGTH'output_field = fields.IntegerField() # sometimes specified the type# lookup_name = 'length' # useful for lookup not for Func usage
查找转换将相同的功能应用于查找的左侧和右侧。
# I'm searching people with usernames longer than mine qs = User.objects.filter(username__length__gt=my_username)
如果未在自定义as_sql()中覆盖,
Func.as_sql(..., function=..., template=..., arg_joiner=...)
则可以已经在其中指定了可以在中指定的相同关键字参数,也可以将Func.__init__()
它们设置为的自定义后代类的属性Func
。许多SQL数据库函数都具有冗长的语法,例如
POSITION(substring IN string)
,如果不支持命名参数(例如POSITION($1 IN $2)
,简短的变体STRPOS(string, substring)
(por postgres)或INSTR(string, substring)
(对于其他数据库)),则会简化可读性Func()
,而Python封装器使用则更容易实现__init__(expression, substring)
。还可以通过将更多嵌套函数与简单参数安全方式结合使用来实现非常复杂的函数:
Case(When(field_name=lookup_value, then=Value(value)), When(...),... default=Value(value))
。



