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

Posted

技术标签:

【中文标题】如何防止 Flask-SQLAlchemy 中的 SQL 注入?有没有更好的方法从 CSV 加载数据?【英文标题】:How do I prevent SQL injections in Flask-SQLAlchemy? Is there a better way to load data from CSV? 【发布时间】:2020-07-17 02:25:10 【问题描述】:

我正在尝试使用 Flask、SQLAlchemy 和 PostgreSQL/psycopg2 将来自 Internet 的数据快速加载到表中。我和一位同事发生了轻微的争吵。我们称他为“爸爸”。爸爸争辩说,由于可能存在 SQL 注入,我们无法执行原始 SQL 查询。我认为我们可以,如果它可能是格式化的,这很难做到,通常应该使用 ORM。下面的例子在我看来是一个足够简单的问题。

# Flask-SQLAlachemy
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy

server = Flask(__name__)
server.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
server.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://...'
db = SQLAlchemy(server)

@server.route('/<column>')
def index(column):
    result = db.session.execute("SELECT" + column + "from item_profit")
    return render_template('index.html', data=result)

基本上有人可以将任何原始 SQL 插入列并返回该表。 有关其他简单注射,请参阅此 link。 我看到了这个 SO answer (参见第二个答案),这似乎暗示格式正确的字符串不会导致 SQL 注入。他们的代码如下所示:

result = db.session.execute('SELECT * FROM my_table WHERE my_column = :val', 'val': 5)

爸爸说很多公司的 DBA 阻止其他部门拥有执行权限,即他们无法使用

    result = db.session.execute("...")

我认为这是因为他们担心普通用户不知道如何正确格式化它。那是对的吗?并不是执行 SQL 查询会导致 SQL 注入,而是 不正确 格式的执行查询会导致 SQL 注入。这就是人们说使用 ORM 的原因,因为它始终确保格式正确的查询。这是正确的想法吗? ORM 系统只是在幕后执行。如果执行查询是一个注入问题,那么即使是 ORM 也无法使用。哎呀,连SQL都不能用,对吧?我希望在某些情况下使用原始 SQL,因为我们可能要处理庞大的数据集,其中处理速度是一个主要因素,而 ORM 则要慢得多。

然后我的后续问题是,将 CSV 加载到 SQL 数据库中同时最小化原始 SQL 的最佳方法是什么?爸爸使用 PgAdmin 导入将 CSV 导入到 postgres(节省了在 psql 中写出导入的大量时间)。我知道另一种方法是使用 pandas pandas.Dataframe.to_sql() 方法,但我不认为为此引入 pandas 似乎是这里最好的设计。我通常使用 ORM,但我不确定是否有一种快速获取 CSV 的方法。假设我有一个表格的 csv,Product,有两列,NameCost。为了在 ORM 中实现这一点,我将执行以下操作:

...

db = SQLAlchemy(app)

class Product(db.Model):
    __tablename__ = 'product'

    name = db.Column(db.String)
    cost = db.Column(db.Double)

但是,我又回到了将 CSV 导入 python 以加载到表中的问题。有来自_csv('')的熊猫。我想我也可以打开文件本身并循环遍历它。类似的东西

for record in csv_file:
    product = Product(name=..., cost=...)
    db.session.add(product)
    db.session.commit()

有没有更好的方法? ORM 是否有办法加载 CSV,同时允许我在大多数时间避免使用原始 SQL?

【问题讨论】:

【参考方案1】:

这是很多问题。

@server.route('/<column>')
def index(column):
    result = db.session.execute("SELECT" + column + "from item_profit")
    return render_template('index.html', data=result)

如果您限制列变量可以包含的内容,这看起来很简单。但问题是你永远不知道代码将来会发生什么。所有可能的检查都可以消失,只有这个“SELECT”+列+“来自...”会留下来,然后可能会出现问题。这就是为什么我总是反对构建 sql 并将其发送到数据库中。

爸爸说很多公司的 DBA 阻止其他部门拥有执行权限,即他们无法使用

我不确定这到底是什么意思。某些公司或某些应用程序可能会阻止数据库运行存储过程以外的任何其他内容。因此数据库不能被滥用,权限更容易检查。但是数据库不能阻止 sqlalchemy 做 db.session.execute。 db.session.execute 可以执行 select、update、delete、存储过程、任何一般的 sql,RDBMS 将向该 sql 应用权限。无论您使用原始 sql,还是您的原始 sql 是由 ORM 或任何其他构建器构建的,都无关紧要。

现在,即使是 SQLAlchemy 也有 2-3 个级别。 ORM 是***别。然后是允许您构建和执行 SQL 的 QL(查询语言)。最后,您可以执行文字 SQL。

在文字 SQL 中可以使用参数,例如: https://docs.sqlalchemy.org/en/13/core/tutorial.html#specifying-bound-parameter-behaviors

在 QL 中,您可以轻松构建如下查询:

q = table_name.insert(
    "id": 3321, "name": python_var_name, "age": python_var_age,
)
conn.execute(q)
# or even session.execute(q)  but consult the docs

在 ORM 中,您已经知道该怎么做。但是由于您要插入大量记录,您可能正在考虑使用 bulk_insert_mapping,https://***.com/a/35495327/3939992,但它会使用您可能不想要的所有 ORM 机制。

使用 QL 会更好。阅读教程,都在里面:https://docs.sqlalchemy.org/en/13/core/tutorial.html https://docs.sqlalchemy.org/en/13/core/tutorial.html#executing-multiple-statements

【讨论】:

哇,我还没有听说过 QL。凉爽的。我会调查的。所以澄清一下,我做的事情是 select 中的列的连接当前不是 SQL 注入,但由于我们不知道未来,如果有人传递会导致不良行为的列值(即访问),它可能会导致问题到我们不想允许的数据。我认为您对 DBA 权限和执行的回答就是我想说的。你就在那里。对不起,如果我解释得不好。导致 SQL 注入的不是 execute(),而是 SQL 本身,需要正确格式化

以上是关于如何防止 Flask-SQLAlchemy 中的 SQL 注入?有没有更好的方法从 CSV 加载数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止从表中已有的值派生的表中的某些值组合?

Flask-SQLAlchemy中的分页操作

Flask-SQLAlchemy:如何更改表结构?

Flask-SQLAlchemy 中的 LocalProxy 对象

如何返回 Flask-SqlAlchemy 错误详细信息

Flask-SQLAlchemy:如何有条件地插入或更新一行