如何安全重写支持任意列的SQLite3表创建Python可变参数函数
如何安全重写支持任意列的SQLite3表创建Python可变参数函数
你碰到的这个问题其实非常常见——SQL的参数化查询机制只支持对数据值使用占位符,像表名、列名这种属于SQL语法结构的部分,数据库驱动是不允许用占位符替换的,这也是为了保证SQL语法解析的正确性。那要怎么在保持函数灵活性的同时,避免SQL注入风险呢?下面给你一套可行的安全方案:
核心思路:先验证,再拼接
要避免注入,关键是确保所有输入的表名、列名、数据类型都是合法的SQLite标识符/类型,绝不允许恶意内容进入最终的SQL语句。具体分两步走:
1. 编写输入验证逻辑
首先我们需要定义规则,过滤掉非法输入:
- 表名、列名必须符合SQLite标识符规范:只能包含字母、数字、下划线,不能以数字开头,同时不能是SQL的保留关键字(比如
CREATE、SELECT、TABLE这类)。 - 数据类型必须限制在SQLite官方支持的范围内:
TEXT、INTEGER、REAL、BLOB、NUMERIC(不区分大小写)。
这里可以写两个辅助验证函数:
import re import sqlite3 def is_valid_identifier(name): # 匹配字母开头,后跟字母/数字/下划线的字符串 if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', name): return False # 检查是否是SQLite保留关键字 cursor = sqlite3.connect(":memory:").cursor() cursor.execute("SELECT name FROM pragma_keywords WHERE name = ? AND type = 'keyword'", (name.upper(),)) return cursor.fetchone() is None def is_valid_type(data_type): valid_types = {'TEXT', 'INTEGER', 'REAL', 'BLOB', 'NUMERIC'} return data_type.upper() in valid_types
2. 重写安全的创建表函数
把验证逻辑整合进原函数,同时用更优雅的方式拼接列字符串(用join代替循环+切片,更简洁):
def create_table(cur, conn, table_name, *columns): # 验证表名合法性 if not is_valid_identifier(table_name): raise ValueError(f"非法的表名:{table_name}") column_defs = [] for col_name, col_type in columns: # 验证列名和数据类型 if not is_valid_identifier(col_name): raise ValueError(f"非法的列名:{col_name}") if not is_valid_type(col_type): raise ValueError(f"非法的数据类型:{col_type}") # 转义标识符(用双引号包裹,避免和关键字冲突) column_defs.append(f'"{col_name}" {col_type.upper()}') # 拼接最终SQL语句 column_string = ", ".join(column_defs) sql = f"CREATE TABLE \"{table_name}\"({column_string})" cur.execute(sql) conn.commit()
测试验证
用你原来的测试代码跑一遍,完全可以正常工作:
# 测试数据库 connection = sqlite3.connect(":memory:") cursor = connection.cursor() # 创建表 create_table(cursor, connection, "employees", ("name", "text"), ("age", "int")) # 检查表结构 cursor.execute("PRAGMA table_info(employees)") print(cursor.fetchall())
输出和你预期的一致:
[(0, 'name', 'TEXT', 0, None, 0), (1, 'age', 'INT', 0, None, 0)]
为什么这样安全?
- 所有输入的表名、列名都经过了严格的格式校验,并且用双引号包裹,就算输入的是类似
users; DROP TABLE users;这种恶意内容,也会被验证函数拦截,不会生成危险的SQL语句。 - 数据类型被限制在合法范围内,避免了通过恶意类型注入的风险。
如果有人尝试传入非法参数,比如create_table(cursor, connection, "123invalid", ("name", "text")),函数会直接抛出ValueError,阻止后续执行。
备注:内容来源于stack exchange,提问作者doggo
相关产品推荐
相关产品推荐

