You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何安全重写支持任意列的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 11:55:28