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

问题描述

我正在尝试使用 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 插入列并返回该表。有关其他简单注射的信息,请参见此链接。我看到了这个 SO答案(见第二个答案),这似乎暗示格式正确的字符串不会导致 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,有两列Name, 和Cost。为了在 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?

标签: pythonsqlalchemy

解决方案


这是很多问题。

@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: //stackoverflow.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 -多重陈述


推荐阅读