HTTP头部参数如User-Agent、Referer等因可被攻击者完全控制却常被误判为可信,导致SQL注入与XSS风险;必须对所有header值统一执行参数化处理,禁用字符串拼接。

因为 HTTP 头部参数(如 User-Agent、Referer、X-Forwarded-For、Cookie)和 $_POST 一样可被攻击者完全控制,但常被开发者误判为“可信来源”,跳过参数化处理——这等于在日志、风控、IP记录等高频路径上主动留后门。
为什么 User-Agent 和 Referer 最容易中招
这两个字段不经过前端表单校验,浏览器自动携带,服务端又习惯性直接取值入库。典型错误场景包括:
- 自研访问统计模块写日志:
INSERT INTO logs (ua, referer, ip) VALUES ('$ua', '$referer', '$ip') - 订单来源识别逻辑:
UPDATE orders SET source = '$referer' WHERE id = ? - 后台管理页展示原始
User-Agent且未 HTML 转义,导致 XSS + SQL 注入双触发
更麻烦的是,WAF 常默认只扫描 GET/POST body,对 header 只做长度截断(比如只检查前 128 字节),而攻击 payload 往往藏在后面。
X-Forwarded-For 和 Cookie 的隐蔽风险
它们表面看是“系统级字段”,实则极易伪造:
-
X-Forwarded-For:curl 可直接构造-H "X-Forwarded-For: 127.0.0.1' OR SLEEP(5)--",若代码里写$ip = $_SERVER['HTTP_X_FORWARDED_FOR']后拼进 SQL,立刻中招 -
Cookie:攻击者能手动设置任意值,比如Cookie: session_id=dumb' UNION SELECT password FROM users--;旧版 ThinkPHP 的where()若传入动态 key,还可能把 Cookie 名当字段名解析
注意:$_SERVER['HTTP_CLIENT_IP']、$_SERVER['HTTP_X_REAL_IP'] 同样可控,不能当作“真实 IP”无条件信任。
参数化不是选配,是唯一安全路径
任何把 header 值塞进 SQL 的地方,都必须走数据库驱动的参数绑定,而不是靠 mysqli_real_escape_string() 或正则过滤:
- PHP PDO:
$stmt = $pdo->prepare("INSERT INTO logs (ua) VALUES (:ua)"); $stmt->execute(['ua' => $_SERVER['HTTP_USER_AGENT']]); - Python PyMySQL:
cursor.execute("INSERT INTO logs (referer) VALUES (%s)", [referer]) - Java JDBC:
ps.setString(1, request.getHeader("User-Agent"));,绝不用"'" + ua + "'" - Node.js mysql2:
conn.query("SELECT * FROM logs WHERE cookie = ?", [cookie])
ORM 框架(Django、SQLAlchemy)虽默认安全,但一旦调用 raw()、extra() 或用 f-string 拼接 SQL,防护即刻失效。
网关层加固不能只靠代码
参数化解决执行逻辑篡改,但挡不住超长 header 拖慢服务或撑爆日志。需在 Nginx / API 网关补防:
- 限制长度:
map $http_user_agent $ua_too_long { ~^.{500,} 1; default 0; },配合if ($ua_too_long) { return 400; } - 白名单校验:
filter_var($referer, FILTER_VALIDATE_URL)后只取 host 做域名比对,非白名单一律置空 - 日志截断:入库前强制截断至 256 字符,数据库字段设为
VARCHAR(255) NOT NULL DEFAULT ''
最容易被忽略的一点:Accept-Language、Authorization、Host 这些字段同样会进 SQL,只要业务代码读了它们,就得一视同仁走参数化——没有“低风险头”这种说法。

















