如何在 Python 中安全地将值传递给 SQLite PRAGMA 语句?

How do you safely pass values to SQLite PRAGMA statements in Python?

提问人:Sam Zuk 提问时间:11/2/2023 更新时间:11/2/2023 访问量:36

问:

我目前正在用 Python 编写一个应用程序,将其数据存储在 SQLite 数据库中。我希望将数据库文件加密存储在磁盘上,我发现最常见的解决方案是 SQLCipher。我将sqlcipher3添加到我的项目中以提供DB-API,然后开始了。使用 SQLCipher 时,数据库加密密钥以 PRAGMA 语句的形式提供,该语句必须在对数据库执行第一个操作之前提供。

PRAGMA key='hunter2';  -- like this

当我的程序运行时,它会提示用户输入数据库密码。我担心的是,由于这是用户输入的来源,因此它可能容易受到 SQL 注入的影响。例如,提供密钥的幼稚方式可能如下所示:

from getpass import getpass
import sqlcipher3

con = sqlcipher3.connect(':memory:')
cur = con.cursor()
password = getpass('Password: ')
cur.execute(f"PRAGMA key='{password}';")

### do stuff with the unencrypted database here

如果有人在密码提示符中输入类似“”的内容,则生成的 SQL 语句在替换后将如下所示:hunter2'; DROP TABLE secrets;--

PRAGMA key='hunter2'; DROP TABLE secrets;--';

通常,此问题的解决方案是使用 DB-API 的参数替换。来自 sqlite3 文档

SQL 语句可以使用以下两种占位符之一:问号(qmark 样式)或命名占位符(命名样式)。对于 qmark 样式,参数必须是长度必须与占位符数匹配的序列,否则会引发 a。对于命名样式,参数必须是(或子类)的实例,该实例必须包含所有命名参数的键;任何额外的项目都将被忽略。下面是这两种样式的示例:ProgrammingErrordict

con = sqlite3.connect(":memory:")
cur = con.execute("CREATE TABLE lang(name, first_appeared)")

# This is the named style used with executemany():
data = (
    {"name": "C", "year": 1972},
    {"name": "Fortran", "year": 1957},
    {"name": "Python", "year": 1991},
    {"name": "Go", "year": 2009},
)
cur.executemany("INSERT INTO lang VALUES(:name, :year)", data)

# This is the qmark style used in a SELECT query:
params = (1972,)
cur.execute("SELECT * FROM lang WHERE first_appeared = ?", params)
print(cur.fetchall())

这在文档的示例代码中按预期工作,但是当在 PRAGMA 语句中使用占位符时,我们得到一个提示,告诉我们存在语法错误。这两种类型的参数替换都是这种情况。OperationalError

# these will both fail

cur.execute('PRAGMA key=?;', (password,))
cur.execute('PRAGMA key=:pass;', {'pass': password})

我不确定从这里去哪里。如果我们实际上在密码提示符下输入恶意字符串,它将不起作用,从而产生以下错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
sqlcipher3.ProgrammingError: You can only execute one statement at a time.

那么,早期的“幼稚”代码安全吗?我不敢说答案是“是”,只是因为我能想出的一个恶意字符串不起作用,但似乎没有更好的方法。这里唯一一个问这个问题的人的答案是我可以找到建议的等效解决方案(python + sqlite insert variable into PRAGMA statement)。我也宁愿不使用 ORM,特别是如果它只用于这种情况。任何建议将不胜感激,谢谢。

python sqlite sql 注入 sqlcipher

评论


答: 暂无答案