第 2 章:HTTP 请求与响应结构——审计的第一道门
做 HTTP 协议审计,你首先得看懂报文长什么样。就像医生看化验单,不懂正常值就找不出异常。
我记得刚入行那会儿,对着抓包工具里密密麻麻的十六进制流一头雾水。后来师傅丢给我一句话:“先学会拆解请求和响应,你就入了门。” 好,今天我们就来亲手拆一遍。
2.1 请求报文:客户端在说什么
一条 HTTP 请求,严格由四部分拼接而成:请求行 → 请求头 → 空行 → 请求体。缺一不可。
2.1.1 请求行(Request Line)
请求行只有一行,格式是:
方法 URI 版本\r\n
举例:GET /api/users?id=1 HTTP/1.1
- 方法:GET、POST、PUT、DELETE 等。审计时我特别关注那些不常见的动词,比如 OPTIONS、TRACE、CONNECT,往往藏着信息泄露或隧道攻击。
- URI:包含路径和查询参数。我曾遇到过攻击者通过编码过的路径遍历字符(
%2e%2e%2f)尝试读取服务器文件。审计时一定把 URI 解码出来看。 - 版本:现在主流还是 HTTP/1.1。如果见到 HTTP/0.9 或 HTTP/1.0 的请求,大概率是恶意扫描器放出来的错包。
注意:请求行必须以
\r\n 结尾,且不允许出现换行符干扰。一旦解析格式错误,服务端可能直接返回 400 或拒绝服务。
2.1.2 请求头(Request Headers)
请求头是键值对集合,每行一个,以 \r\n 分隔。我最关注下面这些头:
| 头字段 | 审计要点 |
|---|---|
Host | 必须存在。很多反向代理、CDN 靠它做路由。攻击者会伪造 Host 头来绕过权限检查。 |
User-Agent | 审计:这个 UA 是不是常见浏览器的?突然冒出一个 python-requests/2.25 就要警惕,可能是爬虫或自动化攻击。 |
Content-Type | POST 请求必须带。攻击者常改 Content-Type 来绕过服务端校验,比如把 application/json 改成 text/plain 加注脚本。 |
Cookie | 审计时重点看 session ID 是否可预测,Secure/HttpOnly 属性是否缺失。 |
Authorization | Base64 编码的 Basic 认证容易被解码;Bearer 需要检查签发和过期时间。 |
我的习惯:在 Wireshark 或 Burp Suite 里,我会专门建一个过滤器只显示
Host、User-Agent、Cookie 三列异常值。这能快速筛掉 80% 的低级扫描。
2.1.3 空行(CRLF)
请求头结束后,必须有一个空行(\r\n)来分隔头和体。为什么要有它?因为解析器读到连续两个 \r\n 就知道头部结束了。我见过攻击者利用空行来注入第二组请求头(HTTP 请求走私),原理就是在这个空行处做手脚。
2.1.4 请求体(Request Body)
只有 POST、PUT、PATCH 等方法会带请求体。常见格式:
- application/x-www-form-urlencoded:
key1=value1&key2=value2 - multipart/form-data:文件上传专用,用边界分隔
- application/json:
{"key":"value"}
审计时要留意大文件和畸形数据。有一次我审计一个上传接口,发现攻击者在图片 EXIF 中嵌入了 webshell,请求体边界被修改导致服务器解析错误——这就是典型的文件上传绕过。
2.2 响应报文:服务器怎么回答
响应报文结构跟请求类似,但第一行变成了响应行:
版本 状态码 原因短语\r\n
例如:HTTP/1.1 200 OK
2.2.1 状态码(Status Code)
状态码分为 5 类,审计时脑子里要立刻反应:
| 类别 | 范围 | 代表含义 |
|---|---|---|
| 1xx | 100–199 | 信息性。比如 101 切换协议。少见,看到就查是不是 WebSocket 升级 |
| 2xx | 200–299 | 成功。200 最常用,204 无内容,206 部分内容(用于断点续传) |
| 3xx | 300–399 | 重定向。301 永久/302 临时。审计时注意 Location 头是否指向恶意域名 |
| 4xx | 400–499 | 客户端错误。403 禁止、404 未找到、429 限流。攻击者经常把 404 伪装成 403 来隐藏路径 |
| 5xx | 500–599 | 服务端错误。500 内部错误、502 网关超时、503 不可用。审计时如果频繁出现,可能是程序漏洞或拒绝服务 |
2.2.2 响应头(Response Headers)
常用的有:
Date:服务器时间,可用来判断时区或发现时间篡改Server:服务器软件版本,应隐藏细节避免信息泄露Content-Type:响应体的 MIME 类型,审计时检查是否有返回敏感文件(如 text/html 下返回了数据库配置文件)Set-Cookie:种 cookie 时必看 Secure、HttpOnly 是否开启Location:重定向的目的地,注意跨域跳转
2.2.3 响应体(Response Body)
响应体就是服务器真正返回的数据。审计时要重点看:
- 是否包含完整的错误堆栈信息(攻击者可利用
- 是否返回了预期的 JSON/HTML,还是意外的调试信息
- 敏感字段(密码、、身份证号)是否被明文返回
我曾有一次审计发现,服务器把 password_hash 直接写在 application/json 响应里,而客户端根本就不需要这个字段——纯粹是偷懒全量返回。
2.3 完整报文示例:看一个真实请求-响应
下面是一个典型的 GET 请求与 200 响应全过程(换行符用 \
请求:
GET /search?q=http+audit HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
Cookie: sessionid=abc123
(空行,这里没有请求体)
响应:
HTTP/1.1 200 OK
Date: Mon, 10 Feb 2025 14:22:00 GMT
Server: Apache/2.4.57 (Unix)
Content-Type: text/html; charset=utf-8
Set-Cookie: sessionid=def456; Path=/; HttpOnly
<!DOCTYPE html>
<html>
<head><title>搜索结果</title></head>
<body>...</body>
</html>
审计关键点:注意请求中 Cookie 被原样返回?sessionid 是否带 HttpOnly?Server 头泄露了 Apache 版本(2.4.57 有已知漏洞 CVE-2023-31122)。这些细节就是审计的突破口。
2.4 知识结构图:一次完整的审计视角
下面这张图把请求和响应的所有组成部分串在一起,方便你看到每个模块怎么关联。
2.5 避坑指南:我曾经踩过的坑
说几个真实审计中容易忽略的点:
- 空行被忽略? 我曾经解析一段流量时,工具显示报文正常,但实际缺少了空行。结果客户端一直报 400 Bad Request。审计时不能只看工具展示,要手动验证
\r\n数量。 - 请求体里的 BOM 头 某些 Java 应用会在 POST 数据前加上 UTF-8 BOM(
EF BB BF),这会导致签名校验失败,攻击者正好利用这个差异绕过 WAF。 - 状态码与响应体不匹配 比如返回 302 重定向,但响应体里却有大量 HTML 内容,可能是中间件在输出前被注入了内容。我遇到过 CDN 缓存污染,就是状态码 404 但响应体却是 200 的内容。
小技巧:在审计日志或抓包时,可以用 awk 类似的方式,只提取第一行和状态码、Content-Length 三个字段,做成表格快速扫描异常。我自己写了个脚本统计每小时的 4xx/5xx 比例,超过 20% 就要立即排查。