7.4.5 在查询中使用变量
如果查询被定义为字面量字符串,要嵌入到程序中,那么使用这种查询会很不灵活。例如,向数据库增加另一个项目时,显示前5个任务的查询就应当更新,从而能处理其中任意一个项目。要向增加灵活性,一种方法是建立一个SQL语句,通过在Python中结合相应的值来提供所需的查询。不过,以这种方式构造查询串很危险,应当尽量避免。如果未能对查询中可变部分的特殊字符正确地转义,则可能会导致SQL解析错误,或者更糟糕地,还有可能导致一类被称为SQL注入攻击(SQL-injection attack)的安全漏洞,这会使入侵者能够在数据库中执行任意的SQL语句。
要在查询中使用动态值,正确的方法是利用随SQL指令一起传入execute()的宿主变量(host variable)。执行SQL语句时,SQL中的占位符值会替换为宿主变量的值。通过使用宿主变量,而不是解析之前在SQL语句中插入任意的值,这样可以避免注入攻击,因为不可信的值没机会影响SQL语句的解析。SQLite支持练个形式带占位符的查询,分别是位置参数和命名参数。
7.4.5.1 位置参数
问号(?)指示一个位置参数,将作为元组的一个成员被传至execute()。
import sqlite3
import sys
db_filename = 'todo.db'
project_name = sys.argv[1]
with sqlite3.connect(db_filename) as conn:
cursor = conn.cursor()
query = """
select id,priority,details,status,deadline from task
where project = ?
"""
cursor.execute(query,(project_name,))
for row in cursor.fetchall():
task_id,priority,details,status,deadline = row
print('{:2d} [{:d}] {:<25} [{:<8}] ({})'.format(
task_id,priority,details,status,deadline))
命令行参数会作为位置参数安全地传至查询,所以恶意数据不可能破坏数据库。
运行结果: