不能直接信任大模型生成的sql,因其常将用户输入直接拼接进字符串导致sql注入;需通过sql沙盒在执行前静态分析,拦截高危关键词、校验参数化格式、检测注释符与分号等风险特征。

为什么不能直接信任大模型生成的SQL
大模型生成SQL时,常把用户输入原样拼进字符串,比如输出 "SELECT * FROM users WHERE name = '" + user_input + "'" 这类模板。它不区分上下文——哪怕你提示“请用参数化”,只要输入里混了单引号、注释符或布尔逻辑片段(如 ' OR 1=1 --),生成结果就可能带注入payload。更危险的是,模型可能“自作聪明”补全语句,把 user_input 当成代码片段而非数据处理。
SQL沙盒校验的核心动作:拦截+解析+约束
沙盒不是运行SQL,而是静态分析其结构与风险特征。关键点在于三步过滤:
- 拒绝含
UNION、EXEC、EXECUTE、DECLARE、CREATE、DROP、ALTER等非查询关键词的语句 - 提取所有字符串字面量,检查是否包含 SQL 注释符(
--、/*)、分号(;)、布尔运算符(OR、AND)出现在引号内但未被转义 - 验证占位符形式:只允许
?、:name、$1等标准参数标记,禁止出现"' + input + '"类拼接模式
示例:模型输出 "SELECT id FROM logs WHERE level = '" + level + "' AND ts > '" + since + "'" 会被直接拒收;而 "SELECT id FROM logs WHERE level = ? AND ts > ?" 才进入下一流程。
在LangChain等框架中嵌入校验层的实际做法
不要依赖LLM自己“说它安全了”。必须在调用插件前加一道中间校验:
- 用
sqlparse(Python)或node-sql-parser(JS)做语法树解析,遍历WHERE子句中的Comparison节点,确认右侧全是参数节点,而非StringLiteral - 对 LangChain 的
SQLDatabaseChain或自定义工具函数,包装一层validate_sql_safety(sql: string): boolean,返回false时抛出ValueError("Unsafe SQL detected")并中断执行 - 禁用
allow_dml=True以外的 DML 操作,尤其防止模型生成INSERT INTO users VALUES (...)后又自动补上SELECT * FROM users—— 这类链式操作需显式白名单控制
最容易被忽略的盲区:ORM映射层与日志回显
即使SQL本身通过了沙盒,风险仍可能从别处漏出:
- ORM 如 SQLAlchemy 的
text()构造器若传入未校验的字符串,等于绕过所有参数化保护;校验必须覆盖text("...")内容本身 - 错误日志如果把原始SQL连同用户输入一起打出来(如
logger.error(f"Query failed: {raw_sql}")),会把注入payload直接暴露在日志系统中,形成信息泄露通道 - 某些数据库驱动(如旧版
psycopg2)支持execute("""SELECT %s""", (user_input,)),但若user_input是"'a'; DROP TABLE users; --",仍会因驱动版本或配置问题执行多语句——沙盒必须检测分号和多语句迹象
真正的防护不在“生成端”,而在“执行前最后一毫秒”。任何跳过校验直连数据库的路径,都是给提示注入留的后门。










