SQLAlchemy + SQL 注入

84 投票
4 回答
68884 浏览
提问于 2025-04-16 20:25

在使用SQLAlchemy时,如何有效防止SQL注入攻击?

4 个回答

17

为了补充一下@Tendrid的回答,我做了一点简单的调查。filter方法有一个叫*criterion的参数,其他一些ORM查询方法也有类似的参数。

filter方法中,*criterion这个参数最终会传递给_literal_as_text,如果传入的是字符串,它会把这个字符串标记为安全的SQL(如果我说错了,请纠正我)。所以这就让它变得不安全了。

下面是对ORM查询类方法的调查结果,涉及到*criterion参数:

filter   - uses _literal_as_text (NOT SAFE)
having   - uses _literal_as_text (NOT SAFE)

distinct - uses _literal_as_label_reference (NOT SAFE)
group_by - uses _literal_as_label_reference (NOT SAFE)
order_by - uses _literal_as_label_reference (NOT SAFE)

join     - uses model attributes to resolve relation (SAFE)

可能的错误用法示例(为了简单起见,这里省略了字符串格式化):

db.session.query(User.login).group_by('login').having('count(id) > 4; select name from roles').all()
db.session.query(User.login).distinct('name) name from roles /*').order_by('*/').all()
db.session.query(User.login).order_by('users_login; select name from roles').all()
db.session.query(User.login).group_by('login union select name from roles').all()

注意:这些方法只有在传入字符串字面量时才是不安全的。

112

总结一下:尽量避免使用原始SQL。

这个被接受的答案其实很懒,而且不正确。filter方法可以接受原始SQL,如果这样使用,就很容易受到SQL注入攻击。举个例子,如果你从网址中获取一个值,然后把它和原始SQL结合在一起使用filter,这样就会有被攻击的风险:

session.query(MyClass).filter("foo={}".format(getArgs['val']))

使用上面的代码和下面的网址,你就会把SQL注入到你的filter语句中。上面的代码会返回你数据库中的所有行。

网址编码:

https://example.com/?val=2%20or%201%20=%201

更容易理解(网址解码后):

https://example.com/?val=2 or 1 = 1
57

如果你的数据中有一些“特殊”的字符,比如分号或者撇号,SQLEngine对象会自动为你加上引号,所以你不需要担心引号的问题。这也意味着,除非你故意绕过SQLAlchemy的引号机制,否则SQL注入攻击基本上是不可能发生的。

[来源于 http://www.rmunn.com/sqlalchemy-tutorial/tutorial.html]

撰写回答