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 注入的主要内容,如果未能解决你的问题,请参考以下文章

sqlalchemy防sql注入

如何防止 Flask-SQLAlchemy 中的 SQL 注入?有没有更好的方法从 CSV 加载数据?

flask-sqlalchemy的使用

flask_sqlalchemy

Python Flask SQLalchemy sqlite3 防止数据库搜索中的 SQL 注入

如何使用SQLAlchemy库写出防SQL注入的Raw SQL