第 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-TypePOST 请求必须带。攻击者常改 Content-Type 来绕过服务端校验,比如把 application/json 改成 text/plain 加注脚本。
Cookie审计时重点看 session ID 是否可预测,Secure/HttpOnly 属性是否缺失。
AuthorizationBase64 编码的 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 类,审计时脑子里要立刻反应:

类别范围代表含义
1xx100–199信息性。比如 101 切换协议。少见,看到就查是不是 WebSocket 升级
2xx200–299成功。200 最常用,204 无内容,206 部分内容(用于断点续传)
3xx300–399重定向。301 永久/302 临时。审计时注意 Location 头是否指向恶意域名
4xx400–499客户端错误。403 禁止、404 未找到、429 限流。攻击者经常把 404 伪装成 403 来隐藏路径
5xx500–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 知识结构图:一次完整的审计视角

下面这张图把请求和响应的所有组成部分串在一起,方便你看到每个模块怎么关联。

HTTP 请求-响应结构总览 客户端 → 请求报文 请求行:GET /search?q=audit HTTP/1.1 请求头(多行) Host, User-Agent, Cookie … 每行以 \r\n 结尾 空行(CRLF) 请求体(可有可无) JSON / 表单 / 二进制 服务器 服务器 → 响应报文 响应行:HTTP/1.1 200 OK 响应头(多行) Date, Server, Content-Type … Set-Cookie, Location 等 空行(CRLF) 响应体(HTML / JSON / 文件) 审计时务必同时检查请求和响应,因为攻击特征往往分布两端

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% 就要立即排查。