主题
HTTP 请求与响应:报文结构详解
上一课说"Web 的本质是客户端发请求、服务端给响应"。这一课把这句话放大一百倍:请求和响应到底长什么样?每个字段是谁写的、写给谁看、有什么用?看完之后,你在 F12 和 Burp Suite 里看到的那堆英文行,会变成认识的老朋友。
一、先拆开一条真实报文
HTTP 报文本质上是纯文本(HTTP/2 是二进制,但结构含义一样),由"起始行 + 头字段 + 空行 + 正文"四段构成:
http
GET /about HTTP/1.1 ← 请求行:方法 + 路径 + 版本
Host: www.myxbw.cn ┐
User-Agent: Mozilla/5.0 ... │ 请求头:一堆"键: 值"
Accept: text/html │
Cookie: sessionid=abc123 ┘
(空行 = 请求头结束的标志,必须有)
(空行之后是请求体,GET 通常为空)
---------- 服务器返回的响应报文,结构完全对称 ----------
HTTP/1.1 200 OK ← 状态行:版本 + 状态码 + 原因短语
Content-Type: text/html; charset=utf-8 ┐
Content-Length: 5321 │ 响应头
X-Frame-Options: DENY ┘
(空行)
<!DOCTYPE html><html>...</html> ← 响应体:真正的网页内容记住这条分界线:空行之前全是"元信息",空行之后才是"正文"。 抓包工具能把它们分色显示,靠的就是这个空行。
💡 动手实验 1:打开
https://www.myxbw.cn,按F12→ Network(网络) → 刷新页面 → 点最上面那条Doc请求 → 右侧 Headers 面板。把 Request Headers 和 Response Headers 抄进记事本,与上面那段代码对照——它们是同一个东西,浏览器只是替你排了版。
二、请求行:方法、路径、版本
请求行就是三个东西:方法(想让服务器做什么)、路径(资源在站内的位置,如 /、/search?q=dns)、版本(HTTP/1.1)。
| 方法 | 语义 | 有请求体 | 典型场景 |
|---|---|---|---|
| GET | 取数据 | 通常没有 | 打开页面、搜索 |
| POST | 提交数据、创建资源 | 有 | 登录、发帖、下单 |
| PUT | 整体替换某个资源 | 有 | 更新一条记录 |
| DELETE | 删除资源 | 通常没有 | 删除一条记录 |
| HEAD / OPTIONS | 只要响应头 / 询问允许哪些方法 | 没有 | 探路、跨域预检 |
安全测试里的高频考点:页面用 GET 就能完成敏感操作。因为 GET 参数会留在 URL、浏览器历史、服务器日志里,还能被别的站用一张图片骗着发出去——这正是 CSRF 的温床。
安全性与幂等性
这是两个必须分清的概念:
- 安全(safe):只是"读",不改变服务器状态。GET、HEAD、OPTIONS 安全。
- 幂等(idempotent):发一次和发十次,服务器最终状态一样。GET、PUT、DELETE 幂等。
| 方法 | 安全性 | 幂等性 | 一句话 |
|---|---|---|---|
| GET | 安全 | 幂等 | 只是看看,看几遍都一样 |
| POST | 不安全 | 不幂等 | 提交一次多一条,重复提交多两条 |
| PUT | 不安全 | 幂等 | 把值设成 100,设十次还是 100 |
| DELETE | 不安全 | 幂等 | 删一次没了,再删还是没了 |
你给知识库配的 block-scanner-paths 防火墙规则,判断依据之一就是"方法与路径的组合像不像扫描器"——扫描器特别喜欢对着一堆路径猛发 GET 和 HEAD。
三、请求头:浏览器写给服务器的小纸条
请求头是元信息的大本营,这些字段你现在就该混个脸熟:
| 头字段 | 作用 | 安全视角 |
|---|---|---|
Host | 我要访问哪个域名(一个节点可能住多个站) | 改它可能打到别的站,即"Host 头攻击" |
User-Agent | 我是什么浏览器、什么系统 | 可随意伪造,扫描器也会自报家门 |
Referer | 我从哪个页面点过来的 | 常被拿来做弱校验,同样可伪造 |
Cookie | 带上服务器之前给我的身份凭证 | 最高价值目标,偷到就等于拿到登录态 |
Content-Type | 我发给你的数据是什么格式 | 决定服务器怎么解析你的输入 |
Content-Length | 请求体有多少字节 | 数值造假可能触发请求走私 |
X-Forwarded-For | 经过代理时的真实客户端 IP | 可伪造,别拿它当安全判据 |
重点说两个。Host:Vercel 一个边缘节点同时服务很多域名,服务器只能靠 Host 判断你要看哪个站——你给 www.myxbw.cn 配了 CNAME,但 Host 里写的仍然是 www.myxbw.cn,不是 CNAME 的目标地址。Cookie:这是浏览器自动带上的,你不写一行代码它也会带,第三阶段的《Cookie 与 Session》会专门讲它的来龙去脉。
四、请求体与三种数据编码
请求体里放"要交给服务器的数据",但怎么放、用什么格式,由 Content-Type 说了算。
1. 表单默认编码 application/x-www-form-urlencoded
http
POST /login HTTP/1.1
Host: www.myxbw.cn
Content-Type: application/x-www-form-urlencoded
Content-Length: 31
username=alice&password=123456像 URL 查询串一样用 & 拼接,特殊字符要转义(空格变 +、& 变 %26)。你在登录框里提交的表单,绝大多数就是这个格式——它也是 SQL 注入里最常见的参数形态。
2. 文件上传 multipart/form-data
http
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----X7MA4YWxk
------X7MA4YWxk
Content-Disposition: form-data; name="file"; filename="a.txt"
hello
------X7MA4YWxk--用一串随机的 boundary 当分隔符,把多个字段切成"分片",这样二进制文件也能塞进去。
3. JSON application/json
json
{
"username": "alice",
"password": "123456"
}现代前后端分离项目的标配。注意 JSON 里的引号和花括号不是什么"代码",只是普通字符——但后端若拿它去拼 SQL,一样会中招。
💡 动手实验 2:按
Win+R输入cmd,敲curl -v https://www.myxbw.cn/回车。-v(verbose)会把你发出的请求头用>开头、把服务器回的响应头用<开头逐行打印出来——这就是 F12 内容的命令行版,没有界面干扰,看得更清楚。
五、状态行与状态码:服务器的一句话总结
状态码是三位数字,第一位决定大类:1xx 信息性(还在处理)、2xx 成功、3xx 重定向(去别处拿)、4xx 客户端错误(你的请求有问题)、5xx 服务器错误(我这边出问题了)。
| 状态码 | 含义 | 关键细节 |
|---|---|---|
| 200 | OK | 正常返回 |
| 301 | 永久重定向 | 请记住新地址,缓存很久 |
| 302 | 临时重定向 | 只是这一次去那边 |
| 304 | 未修改 | 配合缓存,服务器省流量 |
| 308 | 永久重定向(保留方法) | 你的 myxbw.cn → www.myxbw.cn 走的就是它 |
| 400 | 请求有语法错误 | 参数格式不对 |
| 401 | 未认证 | "你是谁?先登录" |
| 403 | 已认证但没权限 | 防火墙拦扫描器的返回就是这个 |
| 404 | 路径不存在 | 也可能被用来隐藏真实资源 |
| 405 | 方法不允许 | 你发 POST,接口只收 GET |
| 500 | 服务器内部错误 | 后端崩了,报错还可能泄露细节 |
| 502 | 网关错误 | 前置代理联系不上后端 |
有两个特别值钱的观察角度:403 vs 404——403 说明"东西在,但不给你看",404 说明"这里啥也没有",扫描器靠这个差异判断真实存在的目录;401 vs 403——401 是"没登录",403 是"登录了也不够格",看到 403 说明你已越过认证层,下一步就是试探越权。
六、响应头与安全响应头
| 头字段 | 作用 |
|---|---|
Content-Type | 响应体是什么格式、什么编码 |
Set-Cookie | 让浏览器种下一个 Cookie(会话的开始) |
Cache-Control | 这个响应能缓存多久、谁能缓存 |
Location | 3xx 重定向要去的新地址 |
你在知识库上配的那几个,属于"写给浏览器的安全指令":
| 头字段 | 作用 | 你的配置 |
|---|---|---|
X-Frame-Options | 禁止别人用 iframe 套你的站 | DENY,防点击劫持 |
Content-Security-Policy | 规定页面只能加载哪些来源的脚本 | 未配,是 XSS 的核心防线 |
Strict-Transport-Security | 强制以后只用 HTTPS 访问 | 防降级与 Cookie 窃取 |
X-Content-Type-Options | 禁止浏览器猜 MIME 类型 | 通常设为 nosniff |
Set-Cookie 值得单独一提,它常带三个安全属性:HttpOnly(JavaScript 读不到,XSS 偷不走)、Secure(只在 HTTPS 下发送)、SameSite(限制跨站携带,缓解 CSRF)。
💡 动手实验 3:在
cmd里敲curl -I https://www.myxbw.cn/(大写-I,只取响应头),看看有没有Set-Cookie和 HSTS。再敲curl -I https://www.myxbw.cn/wp-admin,你亲手部署的那条block-scanner-paths规则会回你一个 403——防火墙这一刻被你抓了个正着。
七、HTTP/1.1 与 HTTP/2 的差别
| 对比项 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 报文形态 | 纯文本,人眼可读 | 二进制帧,人眼看不懂 |
| 并发 | 一条连接同时只处理一个请求,要排队 | 一条连接多路复用,互不阻塞 |
| 头部 | 每次完整发送 | HPACK 压缩,重复头只发索引 |
| 头字段名 | 原样保留 | 统一小写(Host 变成 :authority) |
对你来说最实际的差别是:curl 里能看到明文报文,浏览器里走的是 HTTP/2,所以 F12 显示的是浏览器翻译回来的伪头部——你看到的 :method、:path、:authority 就是 HTTP/2 的写法。
八、为什么"报文长什么样"对安全测试至关重要
- 参数在哪,漏洞就在哪。 所有注入类漏洞(SQL 注入、XSS、命令注入)的入口都是"用户可控的数据流进了程序",而这个数据在报文里只有三处可待:URL 查询串、请求头、请求体。找不到参数,就无处下手。
- 抓包看到的就是这些。 Burp Suite 里你能做的核心动作只有一件——拦截报文 → 改一个字段 → 放行 → 看响应。不认识报文结构,就等于不会用 Burp。
- 前端限制不算限制。 输入框的
maxlength、灰掉的下拉框、JS 校验,全都跑在你自己的浏览器里;用 Burp 拦截改包,这些限制一秒蒸发。这个道理,必须亲手改过一次包才算真懂。
小结
text
一条报文 = 起始行 + 若干头字段 + 空行 + (可选)正文
请求行:方法 + 路径 + 版本 状态行:版本 + 状态码 + 原因短语
方法:GET 读 / POST 写 / PUT 换 / DELETE 删;安全=只读,幂等=发几次结果一样
参数藏在三处:URL 查询串 / 请求头 / 请求体;正文格式由 Content-Type 决定
状态码:2xx 成 / 3xx 跳 / 4xx 你错 / 5xx 我错;403 拦了,404 没有,500 崩了
安全测试的动作:拦截 → 改字段 → 放行 → 观察响应这三件事做完再往下走:F12 里逐条读过一次请求头、curl 里亲眼看过一次响应头、明白参数改在哪就等于漏洞打在哪。
下一课把"域名怎么变成网站"讲透:DNS 记录、HTTPS 证书、把一个静态站从本地发布到公网——见《域名、DNS 与把网站发布上线》。
📎 本课对应知识库阅读:《SQL 注入》——这次请重点看示例里的请求报文,想想
?id=1'这个参数是从报文的哪一行进去的。