sql注入在数据迁移脚本中更危险,因其常绕过审查、使用高权限账号,易致全量拖库或表结构破坏;须禁用字符串拼接、白名单校验表名字段名、参数化处理数据值、废弃addslashes等不安全转义函数,并严格遵循最小权限原则。

数据迁移脚本里混入 SQL 注入点,比 Web 接口更危险——它往往以“一次性任务”为名绕过代码审查,又拥有高权限数据库账号,一旦触发就是全量拖库或表结构破坏。
检查迁移脚本是否用了字符串拼接 execute() 或 cursor.execute()
这是最常见也最致命的漏洞源头。迁移脚本常因“图快”直接用 Python 的 f-string 或 + 拼接表名、字段名、WHERE 条件,而这些内容若来自配置文件、命令行参数或 CSV 头部,就等于把钥匙交给攻击者。
- 错误示例:
cursor.execute(f"UPDATE {table_name} SET status = 'migrated' WHERE id IN ({id_list})")——table_name若取自用户传入的--table参数,id_list若来自未校验的 CSV 文件,就完全失控 - 正确做法:表名/字段名等**结构部分**必须白名单校验(如
if table_name not in ['users', 'orders']:),数据值部分一律用参数化占位符(%s或?) - 注意:MySQL 的
mysqlclient和 PostgreSQL 的psycopg2都不支持对表名做参数化,这点和 WHERE 条件不同,必须单独处理
识别并替换已废弃的转义函数 mysqli_real_escape_string() 或 addslashes()
老迁移脚本常依赖这类函数“消毒”,但它们只对单引号、反斜杠等有限字符转义,无法防御基于注释符(--)、布尔盲注或堆叠查询(;)的攻击,且在多字节编码下还有绕过风险。
- 典型误用:
sql = "SELECT * FROM logs WHERE ip = '" + addslashes(user_ip) + "'"—— 若user_ip是127.0.0.1' --,注释掉后续条件后仍能执行 - 替代方案:直接删除所有
addslashes()、mysql_real_escape_string()调用,改用参数化;若必须动态构造 DDL(如CREATE TABLE),则用预定义模板 + 白名单键名映射(如template_map = {'log_v2': 'CREATE TABLE logs_v2 (...)'}) - 特别提醒:
psycopg2的sql.SQL()和sql.Identifier()可安全拼接标识符,但需显式导入from psycopg2 import sql,不是默认行为
验证迁移脚本使用的数据库连接是否遵循最小权限原则
很多迁移脚本用开发环境的 root 账号直连生产库,导致一个注入点就能 DROP TABLE 或 SELECT * FROM users 全量导出。
- 检查连接字符串中的用户名,确认其权限范围:应仅含
SELECT、INSERT、UPDATE(必要时DELETE),禁止DROP、CREATE、ALTER、EXECUTE - MySQL 示例:运行
SHOW GRANTS FOR 'migrate_user'@'%';,输出中不应出现GRANT ALL PRIVILEGES或包含ON *.* - PostgreSQL 示例:检查
\du migrate_user输出,角色不应是superuser或createdb,且具体表权限应通过GRANT SELECT ON TABLE xxx TO migrate_user;显式授予
真正难防的不是明面上的 ' OR 1=1,而是脚本里那些被当成“配置项”读取的 SQL 片段、从 Excel 导入的 WHERE 条件模板、或者用 Jinja2 渲染的动态建表语句——它们不会出现在常规 SAST 扫描范围内,必须人工逐行核对结构与数据的分离边界。











