很多开发者知道要对用户输入进行SQL注入过滤,却忽略了HTTP头也是攻击入口。攻击者能通过User-Agent、Referer、Cookie等头部字段注入恶意SQL代码,如果后端直接将这些头部值拼接进数据库查询,就会导致SQL注入。防护的核心思路是:对所有HTTP头部值进行严格的验证、转义或参数化处理,绝不信任任何外部传入的数据。

HTTP头注入SQL的原理与常见攻击向量

HTTP请求中的头部字段通常由客户端控制或修改。例如,User-Agent可以被篡改为包含SQL片段的字符串,如' OR '1'='1。如果应用代码这样写:String query = "INSERT INTO logs (user_agent) VALUES ('" + request.getHeader("User-Agent") + "')";,恶意值就会破坏查询结构。同样,X-Forwarded-For、Referer、甚至自定义头部都可能成为注入点。攻击者利用Burp Suite等工具可轻松修改头部,探测漏洞。

第一道防线:严格的输入验证与白名单机制

对所有HTTP头部值实施验证。根据业务逻辑,设定允许的字符范围和长度。例如,User-Agent通常只包含字母、数字、空格和特定符号,可用正则表达式进行匹配。对于已知的固定值(如某些内部自定义头),采用白名单机制,只接受预定义的选项。验证应在应用层最早阶段进行,无效值立即拒绝。

// 示例:User-Agent格式验证(Java)
String userAgent = request.getHeader("User-Agent");
if (!userAgent.matches("[a-zA-Z0-9\\s.,;:!?()@*/+-]{1,500}")) {
    // 记录日志并拒绝请求
    throw new IllegalArgumentException("Invalid User-Agent header");
}

第二道防线:参数化查询(预编译语句)

这是防止SQL注入最有效的方法。无论数据来自何处,都使用参数化查询,确保数据始终被当作参数处理,而非SQL代码的一部分。所有主流数据库和编程语言都支持此功能。例如,在Java中使用PreparedStatement,在PHP中使用PDO。绝对避免字符串拼接构建SQL。

// 示例:使用PreparedStatement处理头部值(Java)
String sql = "INSERT INTO access_logs (user_agent, ip) VALUES (?, ?)";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, request.getHeader("User-Agent")); // 自动转义
stmt.setString(2, request.getHeader("X-Forwarded-For"));
stmt.executeUpdate();

第三道防线:对动态拼接场景进行转义

在极少数无法使用参数化查询的遗留场景中,必须对头部值进行严格的转义。使用数据库驱动提供的专用转义函数(如MySQL的mysql_real_escape_string()),而不是自行编写替换函数。注意,转义是最后的手段,且必须考虑数据库字符集。

// 示例:使用ESAPI进行转义(Java)
import org.owasp.esapi.ESAPI;
String safeUserAgent = ESAPI.encoder().encodeForSQL(
    new MySQLCodec(), 
    request.getHeader("User-Agent")
);

第四道防线:最小化权限与日志安全

连接数据库的账户应遵循最小权限原则,只授予必要权限(如仅能INSERT日志表,不能DROP)。此外,记录日志时需小心:如果直接将未处理的头部值写入日志文件,可能造成日志注入或干扰监控系统。建议在日志中对非打印字符进行编码。

第五道防线:安全编码框架与中间件防护

采用成熟的安全框架(如Spring Security、OWASP ESAPI)可自动处理部分防护。同时,在Web服务器或应用防火墙(WAF)层配置规则,检测HTTP头中的可疑SQL关键词(如UNION、SELECT、'--')。但WAF只是补充,不能替代代码层安全。

第六道防线:自动化测试与持续监控

将HTTP头注入测试纳入安全测试流程。使用DAST工具(如OWASP ZAP)自动化扫描头部漏洞。代码审查时重点检查所有使用头部值的数据库操作。监控生产环境中的异常查询日志,设置警报机制。

总结:构建纵深防御体系

防止通过HTTP头的SQL注入,需要多层防护:从输入验证、参数化查询,到权限控制和监控。关键是将所有HTTP头部视为不可信输入,并强制所有数据通道都经过安全处理。定期更新安全知识库,对开发团队进行专项培训,才能从根本上消除这类漏洞。