SQL 数据格式化和 SQL 注入

sql data formatting and sql injections

提问人:Diurnambule 提问时间:7/11/2022 更新时间:7/13/2022 访问量:437

问:

我有一个包含 2 个表的数据库:我想更新其中一个表:students, employees

import sqlite3

db_file = "school.db"

def update_address(identifier, user_address, user_id):
    with sqlite3.connect(db_file) as conn:
        c = conn.cursor()
        c.execute(f"""
        UPDATE {identifier}
        SET address = ?
        WHERE id = ?;
        """,
        (user_address, user_id))

update_address("students", "204 Sycamore Street", 2)

上面的代码有效,问题是我知道在 sql 操作中使用 python 字符串格式可能会导致每个 sqlite3 文档的漏洞:

通常,您的 SQL 操作需要使用 Python 变量中的值。不应使用 Python 的字符串操作来组装查询,因为这样做是不安全的;它使您的程序容易受到 SQL 注入攻击(有关可能出错的幽默示例,请参阅 https://xkcd.com/327/)。

请改用 DB-API 的参数替换。放?作为要使用值的任何位置的占位符,然后提供值的元组作为游标的 execute() 方法的第二个参数。

占位符“?”在插入值时有效,但不适用于 sql 标识符。输出:sqlite3.OperationalError: near "?": syntax error

所以这里的问题是:如果我在 sql 标识符上使用 python 字符串格式,是否会发生 sql 注入,或者它是否只发生在值上?

如果它也发生在标识符上,有没有办法以安全的方式格式化字符串?

python sql sqlite sql 注入

评论

2赞 Shawn 7/11/2022
不能使用表/列名作为绑定参数;当表达式由 SQLite 编译时,必须知道它们,这发生在绑定之前。所以,是的,在将标识符字符串插入查询之前,您需要对其进行清理。Sqlite3 的 C API 提供了一些函数来帮助解决这个问题,但我认为它们(就像它能做的大部分事情一样)不是由 Python 提供的。

答:

1赞 Bill Karwin 7/13/2022 #1

是的,如果您不安全地将任何内容插入 SQL 查询,则属于 SQL 注入漏洞。无论内容是否应该用作 SQL 表达式中的值,还是标识符、SQL 关键字或其他任何内容,都无关紧要。

如果要编写具有一组可变条件的查询,则从 SQL 表达式片段中格式化查询是很常见的。这些也是可能的 SQL 注入风险。

降低 SQL 注入风险的方法是:不要将不受信任的输入插入到 SQL 查询中。

对于标识符,应确保内容与表(或列或其他元素,如果这是您尝试动态的)的合法名称匹配。即,创建一个“允许使用函数更新的数据库中已知存在的表的”允许列表”。如果输入与其中之一不匹配,则不要运行查询。

使用反引号来分隔标识符也是一个好主意,因为如果其中一个表名恰好是 SQLite 中的保留关键字,这将允许在 SQL 查询中使用该表。

    if identifier not in ["table1", "table2", "table3"]:
        raise Exception("Unknown table name: '{identifier}'")
    c.execute(f"""
    UPDATE `{identifier}`
    SET address = ?
    WHERE id = ?;
    """,
    (user_address, user_id))