必须改用参数化查询,因Header字段如User-Agent、Cookie、Referer与表单参数同样危险;直接拼接会导致SQL注入,如' OR '1'='1破坏查询逻辑,且报错型、时间盲注均有效;修复须统一走PreparedStatement、%s占位符、PDO绑定等参数化路径,禁用字符串拼接及旧式转义;还需在网关层限长、白名单校验、日志截断、字段类型约束,并覆盖Cookie/Referer检查。

必须改用参数化查询,Header字段(如 User-Agent、Cookie、Referer)和表单参数一样危险,不能因为“不在URL里”就跳过防御。
为什么直接拼接 User-Agent 字符串会出事
后台代码如果写成类似 "SELECT * FROM logs WHERE ua = '" + request.headers.get('User-Agent') + "'",攻击者只要在 User-Agent 里塞 ' OR '1'='1,整个查询逻辑就被破坏。报错型注入(比如用 updatexml())在 Header 场景下一样生效,且更难被日志或WAF覆盖到。
常见错误现象包括:
- 修改
User-Agent为单引号后返回数据库报错(如 MySQL 的You have an error in your SQL syntax) - 页面无报错但响应时间异常(time-based 盲注迹象)
- 登录/统计/风控模块突然失效,但其他功能正常(说明 Header 处理路径独立且未加固)
修复核心:所有 Header 字段必须走参数化查询路径
Header 数据不是“可信元数据”,它和 $_GET 或 $_POST 一样属于不可信输入。修复不是加一层 mysql_real_escape_string() 或正则过滤,而是彻底隔离 SQL 结构与数据。
实操建议:
- Java(JDBC):用
PreparedStatement,setString(1, request.getHeader("User-Agent")),绝不用字符串拼接 - Python(PyMySQL/psycopg2):用
%s占位符或命名参数,如"WHERE ua = %s",传参用(user_agent,) - PHP(PDO):绑定参数,
$stmt->bindParam(':ua', $ua, PDO::PARAM_STR),禁用mysql_*函数 - Node.js(pg/mysql2):使用参数化接口,如
client.query('SELECT * FROM logs WHERE ua = $1', [ua])
注意:ORM 框架(如 Django ORM、SQLAlchemy、Sequelize)默认支持参数化,但若手动拼接 raw() 查询,同样会中招。
额外加固点:Header 字段长度与内容白名单
Header 注入常配合超长 payload 触发缓冲区溢出或拖慢服务,单纯参数化还不够。
建议补充以下控制:
- 在反向代理(Nginx)或网关层限制
User-Agent最大长度,例如limit_req_status 400配合map判断长度 - 对已知合法的
Accept、Content-Type做白名单校验,非法值直接 400 拒绝,不进业务逻辑 - 记录原始
User-Agent到日志时,先做截断(如前255字符),避免日志注入或磁盘打满 - 数据库字段类型设为
VARCHAR(255)而非TEXT,物理上卡住超长输入
别漏掉 Cookie 和 Referer 这两个高危 Header
Cookie 和 Referer 同样高频用于身份校验和来源追踪,一旦拼进 SQL 就是等同风险。尤其 Cookie 中的 session_id 或 token,常被误认为“服务端生成所以安全”,但攻击者可篡改请求头重放。
检查点:
- 所有读取
request.cookies.get('xxx')并参与 SQL 查询的地方 - 所有基于
Referer做跳转白名单或统计归因的 SQLINSERT/UPDATE - 是否在存储过程或触发器里隐式引用了 Header 变量(极难发现,需查 DBA 脚本)
Header 注入最难排查的点在于:它不走常规参数解析链路,容易被开发和测试忽略。上线前务必用 Burp Repeater 手动改 User-Agent 和 Cookie 发一次单引号测试,比任何文档都管用。

















