SQLAlchemy + SQL 注入
Posted
技术标签:
【中文标题】SQLAlchemy + SQL 注入【英文标题】:SQLAlchemy + SQL Injection 【发布时间】:2011-09-24 00:01:21 【问题描述】:在使用 SQLAlchemy 时缓解 SQL 注入攻击的最佳做法是什么?
【问题讨论】:
使用 SQLAlchemy 是最佳实践。 :-) SQLAlchemy 应该有一个页面记录这些最佳实践,例如 Ruby on Rails。 【参考方案1】:如果您有任何“特殊”字符(例如分号或 撇号)在您的数据中,它们将 自动为您报价 SQLEngine 对象,所以你不必 担心报价。这也意味着 除非你故意绕过 SQLAlchemy 的引用机制, SQL注入攻击基本上是 不可能。
[根据http://www.rmunn.com/sqlalchemy-tutorial/tutorial.html]
【讨论】:
答案说引用来自“the”文档,但不是:它似乎来自a tutorial,与 SQLAlchemy 无关。其次,引用在 SQLAlchemy API 的一部分的上下文中,它将正确处理转义,使用处理转义的示例。但是,您仍然可以使用execute()
或其他不会被 SQLAlchemy 转义的文字数据。是的,在大多数情况下,SQLAlchemy 会自动转义,但如果您使用文字或原始 SQL,您仍然可以自取其辱。
我正在寻找 SQLAlchemy 代码仓库中的特定代码行,以证实 @carson ip 引用的内容是 SQLAlchemy 的“引用机制”有什么提示吗?【参考方案2】:
tldr:尽可能避免使用原始 SQL。
接受的答案是懒惰且不正确的。 filter 方法接受原始 SQL,如果以这种方式使用,则完全容易受到 SQL 注入攻击。例如,如果您要接受来自 url 的值并将其与过滤器中的原始 sql 结合起来,那么您很容易受到攻击:
session.query(MyClass).filter("foo=".format(getArgs['val']))
使用上面的代码和下面的 url,您将在过滤器语句中注入 SQL。上面的代码将返回数据库中的所有行。
网址编码:
https://example.com/?val=2%20or%201%20=%201
更容易理解(URL 解码):
https://example.com/?val=2 or 1 = 1
【讨论】:
“除非你故意绕过 SQLAlchemy 的引用机制......” 是的,输入原始 sql 是故意绕过该引用机制。所以不,上面的答案没有错。 我不同意。您可以将原始 sql 传递给 filter 方法是 sqlalchemy 的一部分,而不是一些最终的 hack...所以这里值得注意的是要注意的事情。 如果我必须为过滤器接收用户输入,那么确保用户没有输入原始 SQL 来删除表或任何其他意外行为的正确方法是什么? @divide_by_zero 使用 orm 过滤器方法,这就是它们的用途。永远不要使用原始 sql。 @divide_by_zero 好好使用这个session.query(MyClass).filter(MyClass.foo == "".format(getArgs['val']))
如果你尝试注入一些东西,这可能会抛出 psycopg2.InternalError 无效语法【参考方案3】:
添加到@Tendrid answer。我使用安静天真的方法做了一些调查。 filter
方法的参数是 *criterion
,其他几个 ORM 查询方法也有类似的参数。
如果filter
方法*criterion
参数最终传递给_literal_as_text,如果是字符串- 将其标记为安全sql(如果我错了请纠正我)。因此,它使其不安全。
这是ORM Query class 方法调查的结果,带有*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()
注意,这些方法只有在传递字符串字面量时才是不安全的。
【讨论】:
以上是关于SQLAlchemy + SQL 注入的主要内容,如果未能解决你的问题,请参考以下文章
如何防止 Flask-SQLAlchemy 中的 SQL 注入?有没有更好的方法从 CSV 加载数据?