搞安全的绕不开一个基本功:看懂HTTP数据包,学会自己构造请求。Day 10这节笔记就是围绕HTTP数据包、Postman构造请求、请求方法、请求头修改、状态码判断这几块展开的。说实话,当时学到这里我才算真正“上手”,前面看了不少概念,但动手一抓包、一发请求、一改请求头,整个协议从抽象变成具象。这篇内容是把那天的学习过程完整梳理了一遍,适合刚入门Web安全和接口调试的同学,也适合做前端、后端、测试的朋友当一份HTTP速查笔记用。学完你至少能做到:打开Postman不发怵、抓包看到请求不懵圈、遇到4xx/5xx能立刻定位排查方向。
1. 先搞懂HTTP数据包:一次请求-响应到底长什么样
1.1 请求包拆解:你发给服务器的“订单”
HTTP协议本质就是客户端和服务端之间交流的语言。你发一个请求,等于走进一家餐厅,跟服务员说“我要点菜”,服务员记录完订单,后厨再给你端菜。整个交流过程,就是一次HTTP请求-响应。
一个标准HTTP请求包由四部分组成:请求行、请求头、空行、请求体。用大白话解释:
- 请求行:告诉服务器“我要干什么”,包括请求方法、URI路径、协议版本。
- 请求头:告诉服务器“我是谁、我能接受什么、我从哪来”,是一堆键值对。
- 空行:一个分隔符,告诉服务器“请求头结束,下面开始是正文”。
- 请求体:携带具体数据。GET请求通常没有请求体,POST请求经常有。
我给你看一个最典型的GET请求包,我们平时用浏览器访问网页时,发出的包大概就是这个样子(可以用抓包工具看到):
GET /index.php?id=1 HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.9 Accept-Encoding: gzip, deflate, br Connection: keep-alive Cookie: PHPSESSID=abc123第一行GET /index.php?id=1 HTTP/1.1就是请求行。GET是方法,/index.php?id=1是路径和参数,HTTP/1.1是版本。中间每一行都是请求头。最后这个包里没有请求体,因为GET的参数在URL里面带着了。
再看一个POST请求包的示例,请求体就会出现在末尾:
POST /api/login HTTP/1.1 Host: www.example.com Content-Type: application/x-www-form-urlencoded Content-Length: 29 Cookie: session=xxx username=admin&password=123456注意区别:POST请求把数据放在最后的请求体里了,而Content-Type告诉服务器“我用什么格式编码的”,Content-Length告诉服务器“请求体有多长”。这都是很有用的判断点,后面讲状态码和排查问题时还会用上。
1.2 响应包拆解:服务器给你的“回执”
有请求就有响应,服务器收到订单后会回应你。响应包也是四部分:状态行、响应头、空行、响应体。
一个典型的响应包长下面这样:
HTTP/1.1 200 OK Server: nginx/1.18.0 Date: Mon, 20 Mar 2023 08:00:00 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 128 Set-Cookie: token=example123; Path=/ <html> <head><title>Success</title></head> <body>欢迎回来</body> </html>第一行HTTP/1.1 200 OK是状态行,200是状态码,OK是原因短语。往下是响应头,像Server告诉我们服务器软件类型,Set-Cookie是服务器给客户端发Cookie的指令。空行下面就是响应体,也就是页面实际内容或接口返回的数据。
学习安全为什么非得抠数据包?因为所有Web漏洞本质上都体现在请求和响应的交互中。比如SQL注入,你得先会看请求参数在哪;比如越权,你得能改请求中的ID值看响应变化;比如判断后台路径,你得看403和404的区别。不会看包,后面全都是空中楼阁。
提示:抓包工具有很多,浏览器的F12开发者工具里的Network面板是最快的,适合入门。想要更专业,可以用Burp Suite或Fiddler。Day 10主要配合的是Postman来构造请求,但理解包结构是一切的基础。
1.3 为什么要手动构造而不是只用浏览器
浏览器帮我们做了太多事情:自动添加请求头、自动处理Cookie、自动跳转、自动渲染页面。用浏览器测试时,你根本看不到协议层发生了什么。Postman这类工具的定位就是“把请求的控制权交还给你”——每一个请求头都能改,每个参数都能手动设置,方法随意切换,响应原样展示。
这对安全学习特别重要。举个例子,你在浏览器里点一个按钮,前端可能发了一个POST请求,数据格式是JSON,带着认证Token,但你看不到细节。用Postman复现时,你得自己把URL、方法、请求头、Body全部构造出来,这个过程会逼着你把HTTP协议搞透。我们下面就从Postman的使用开始,一步步来。
2. Postman构造请求:把控制权拿回自己手里
2.1 Postman到底是什么,为什么选它
Postman是一个API调试工具,核心功能就是手动构造HTTP请求、发送给服务器、完整展示响应。它的地位相当于“手动机枪”之于“自动步枪”——浏览器是一把自动步枪,扣一下扳机全自动打完,但你想打哪一枪、用什么子弹,它不让你管;Postman让你每一发子弹都自己装填,指哪打哪。
学习阶段选Postman有几个好处:
- 免代码,所有操作都是填表,新手友好。
- 历史记录、集合管理,可以保存各种测试请求。
- 支持环境变量、断言、自动测试,后期接口自动化也能用。
- 支持导入cURL命令,从浏览器复制的请求可以直接变过来。
安装没什么好说的,官网下载对应平台的版本,一路下一步。国内网络环境下可能下载慢,但官方渠道最稳。安装完打开,主界面就是URL输入框、方法下拉框、Send按钮,很多地方长得像浏览器。
2.2 构造第一个GET请求:三步走
我建议所有人都从GET请求开始练。新建一个标签页(默认就是Untitled Request),然后在URL栏输入一个公开接口地址,比如https://www.baidu.com,方法保持GET,点Send,不出意外你就能在下方看到返回的响应。
整个过程不到五秒,你会看到Response区出现一坨HTML。这里注意看两个东西:
- 状态码:显示为
200 OK,说明请求成功。 - Response body:显示服务器返回的具体内容。
如果你用的是Postman较新版本,界面区分成几个区域:Params、Headers、Body、Pre-request Script、Tests。GET请求的参数怎么加?可以手动在URL里拼?key=value,也可以在Params标签页里添加,Postman会自动拼到URL后面。
来一个稍微贴近接口调试的例子。假设有一个天气查询接口http://api.example.com/weather?city=beijing,我们想要查询北京和上海两个城市的天气,那就把city的值分别改成beijing和shanghai,发送两次,对比响应差异。这你就体会到了参数变化对响应的影响。
2.3 构造POST请求:这里坑最多
POST请求比GET麻烦在请求体格式。新建请求后,把方法从GET切到POST,界面下方会出现Body标签页。Body有几种格式,我们必须搞清楚它们的区别:
| Body格式 | Content-Type | 适用场景 |
|---|---|---|
| none | 无 | 不需要请求体 |
| form-data | multipart/form-data | 文件上传、混合表单 |
| x-www-form-urlencoded | application/x-www-form-urlencoded | 普通表单提交 |
| raw-JSON | application/json | 目前最常见的API接口格式 |
| raw-Text | text/plain | 纯文本 |
| raw-XML | application/xml | XML接口 |
表单提交用x-www-form-urlencoded,就是key=value&key=value这种格式,和老牌Web表单一样。上传文件用form-data。而现代API接口,尤其是前后端分离项目,绝大多数用raw里的JSON格式。
我当初踩过一个坑:拿着一个JSON格式的POST请求,选了x-www-form-urlencoded,在Key-Value表格里填了{"name":"test"},结果服务端怎么都解析不到参数,一直报参数缺失。后来才明白,选了JSON格式,就要去raw里直接写JSON文本,而不是在Key-Value表格里写。
实际操作时注意:
- 选了什么Body格式,Postman会自动帮你设置对应的Content-Type,不需要手动改。
- 如果服务端一直报错,第一件事先看请求头里的Content-Type是不是服务端期望的。
- 带中文参数时,建议确认编码是UTF-8,否则服务端解析乱码,接口直接报错。
2.4 进阶功能:Headers、Authorization、环境变量
Postman真正值钱的地方在Headers标签页。在这里你可以任意添加、修改、删除请求头。比如你要测试一个需要登录的接口,就必须添加Authorization: Bearer <token>这样一个请求头。在Headers表格里加一行Key为Authorization、Value为Bearer xxxxx即可。
环境变量是个好东西。开发环境、测试环境、生产环境换着连的时候,不可能每次都去改URL。Postman可以设置环境变量,比如{{base_url}}/api/login,把base_url定义成不同值,切换环境就切换了整组请求的目标地址。这与配置管理的思想类似,测试多环境接口时非常香。
另外一个强烈推荐的功能:导入cURL。在Postman左上角点Import,选择Raw text,把浏览器复制来的cURL命令粘进去,Postman会自动解析成一个完整请求,URL、请求头、Cookie、Body全给你拆好。这对复现浏览器里遇到的请求,特别是带了一堆复杂请求头的情况,效率极高。
3. 请求方法对比与请求头修改:控制“怎么问”
3.1 GET和POST的区别:不止一个在URL一个在Body
这几天学习印象最深的一件事,就是GET和POST的差异远不止“参数位置不同”这一个维度。我整理了一张对比表,方便直接抄作业:
| 对比项 | GET | POST |
|---|---|---|
| 参数位置 | 请求行的URL中 | 请求体中(也可放在URL) |
| 可见性 | 地址栏可见,会出现在浏览器历史、日志中 | 不在URL显示,但不代表安全 |
| 缓存 | 浏览器会主动缓存GET请求 | 一般不会缓存 |
| 幂等性 | 同一个GET请求发N次,结果应该一样 | 不一定,每次可能产生新状态 |
| 请求体 | 一般没有 | 有 |
| 数据长度 | 受URL长度限制 | 理论上更宽松 |
安全学习的角度来看,有几个细节值得琢磨:
- 敏感数据如果走GET,地址栏、访问日志、历史记录都会暴露,可能有人截图泄露。
- 登录、注册、提交订单这些操作,用GET在语义上就不对,因为GET本意是“获取”,不是“提交”。
- 改包测试时,同一个URL用GET访问和用POST访问,服务端可能返回完全不同的状态码。有些服务端代码只允许POST,你拿GET访问它就是
405 Method Not Allowed。
除了GET和POST,还有PUT、HEAD、DELETE、OPTIONS、PATCH等。但说句大实话,日常学习阶段把GET和POST吃透就够用了,其他的遇到再补,不丢人。
3.2 常见请求头:每一个字段都是一道潜规则
请求头里面门道太多了。学习这部分时,我的心得是不要死记,要理解“这个头是在跟服务器说什么”。常见请求头如下:
| 请求头 | 含义 | 改动场景 |
|---|---|---|
| Host | 要访问的域名和端口 | 测试虚拟主机、排查代理问题 |
| User-Agent | 客户端标识,比如浏览器版本 | 有些站点会根据UA返回不同内容 |
| Referer | 来源页面地址 | 有些接口校验来源,不放或放错就拒绝 |
| Cookie | 登录凭证、会话标识 | 需要带登录态访问时修改 |
| Content-Type | 请求体格式 | 切换Body格式时自动变化 |
| Accept | 客户端能接收的内容类型 | 部分接口返回JSON还是HTML取决于它 |
| Authorization | 认证凭证,通常是Token | 带权限接口必改 |
| X-Forwarded-For | 客户端原始IP | 有些服务端靠它判断IP来源 |
请求头修改是安全测试和接口调优里非常核心的能力。举一个我实际遇到的例子:有个接口,用浏览器访问时返回正常数据,但用Postman请求时却返回403。对比请求头发现,浏览器自动带了Referer和完整UA,而Postman默认没有Referer。把这个头补上之后,请求立刻通过了。这就是请求头修改最朴素的价值。
另外一个例子是UA:有些服务端做了移动端适配,会根据User-Agent返回不同的页面或接口格式。如果你把自己UA改成iPhone的UA,可能拿到完全不一样的响应内容。判断请求头的作用时,我们要学会“对比法”——一次改一个字段,看响应变化,这样定位最准。
3.3 在Postman和抓包工具里改请求头
在Postman里改请求头是最简单的:切到Headers标签页,Key填请求头名,Value填值,点Send就生效。我常用的三类修改场景,你可以照着试:
- 换UA:在Headers里加一行
User-Agent: iPhone,然后访问一个区分设备的网站,响应内容会和默认UA不同。 - 加Token:登录一个系统,拿到的Token填进
Authorization请求头,就能访问需要登录的接口。 - 伪造来源:有些接口校验
Referer,你填了对应来源才能访问。
如果你想用抓包工具(如Burp Suite)改请求头,思路其实一样:浏览器设置了代理,流量经过Burp,你拦截请求后手动修改请求头的值,再放行。这就是“中间人改包”的思路,比Postman更灵活,因为是在真实浏览器环境里操作,但学习成本更高。Day 10学的Postman,已经足够我们理解请求头的作用了。
提示:修改请求头时建议一次只改一个变量。同时改两个字段,如果你发现响应发生了变化,你无法确定是哪一个字段起了作用。这是做任何实验都适用的基本原则——控制变量。
3.4 从浏览器复制请求到Postman的快捷技巧
遇到一个页面请求,想放到Postman里研究,最快的操作是:按F12打开开发者工具,切到Network,找到对应的请求,右键点Copy,选择Copy as cURL,然后在Postman里Import粘贴。这样URL、方法、请求头、Cookie、请求体全部完整复刻,一个不漏。
这个操作我后面用得非常频繁,因为实际系统里的请求往往带了十多个请求头,手敲很容易漏。复制过来后,再逐个修改要改的字段,效率高得多。我建议每个学HTTP的人都要把这个操作形成肌肉记忆。
4. 状态码判断:一眼看出服务端的“态度”
4.1 五类状态码:从1xx到5xx的完整套路
状态码是服务器给客户端的“态度反馈”,三位数字,第一位就代表大的分类。这一块是排查问题最依赖的基础知识。
| 状态码 | 类别 | 含义 | 常见场景 |
|---|---|---|---|
| 1xx | 信息提示 | 收到请求,正在处理 | 100 Continue,比较少关注 |
| 2xx | 成功 | 请求已被接收、理解、接受 | 200 OK,一切正常 |
| 3xx | 重定向 | 需要进一步操作 | 301、302、304 |
| 4xx | 客户端错误 | 请求有问题 | 403、404、405、429 |
| 5xx | 服务端错误 | 服务器处理时出错 | 500、502、503 |
我用一个生活化的类比来记:2xx相当于餐厅说“您的菜上齐了”;3xx相当于“您坐错桌了,请到10号桌”;4xx相当于“您点菜的方式不对,不是我们菜单里的菜”;5xx相当于“后厨出问题了,菜做不出来”。
4.2 安全测试中最常打交道的状态码
学习和工作中的大量请求会遇到下面这些状态码,每一个背后的含义都不一样:
200 OK:请求成功,服务器正常返回了内容。这是最常规的状态,但不代表内容没问题。比如你访问一个不存在的路径,但网站把所有404都返回200加一个“页面不存在”文案,那状态码就是200,原因是服务端做了友好化处理。
301/302:重定向。301是永久重定向,302是临时重定向。常见场景:访问http://被跳转到https://,或者登录后跳转回首页。排查问题时,如果看到响应头里有Location字段,那就是要跳转的目标地址。
304 Not Modified:命中缓存。服务器告诉客户端“你缓存的内容还是最新的,直接用缓存就行”,这个状态码在性能优化时很常见。
403 Forbidden:服务器理解你的请求,但是拒绝执行。这个状态码含义非常丰富——可能是没有权限访问目录,可能是IP被限制,也可能是请求头不对。遇到403时,我会先检查:有没有带认证信息?UA是不是被拒绝的?是不是需要Referer?都在并发这个方向排查。
404 Not Found:找不到资源。但要注意,有些系统为了保护真实文件,故意对不存在的路径也返回404,这时候就不能简单认为“路径不存在”了。相反,如果你访问一个目录时原来返回403(存在但不允许),但改成某些特殊路径后返回404(不存在),这种状态码的差异反映的就是服务端处理逻辑的差别。
405 Method Not Allowed:方法不允许,接口只接受POST,你用GET访问就会这样。
429 Too Many Requests:请求太频繁,被限流了。做接口测试时如果并发拉满,很容易撞到这个。
500 Internal Server Error:服务器内部错误,代码抛异常了。这个状态码是我们后端调试的“老朋友”,大部分情况意味着有逻辑BUG或者数据库连接问题。
502 Bad Gateway:网关/代理收到了上游服务器的无效响应。通俗说,请求已经到达了中间层(比如Nginx),但中间层往后端转的时候,后端根本没给有效回应。
503 Service Unavailable:服务器暂时无法处理,通常在维护或过载时出现。
504 Gateway Timeout:网关等后端响应等超时了。
524 A Timeout Occurred:这个不是标准HTTP状态码,是Cloudflare这类CDN的自定义状态码,表示源站处理超时,和504类似,但在CDN层先判定超时了。不少新手看到524一脸懵,因为RFC没有定义它,查不到就以为见鬼了。其实只要明白“源站太慢/CDN等不下去”这个逻辑就行。
4.3 怎么用状态码判断服务端逻辑
学习状态码不是为了背,而是为了判断。一次请求中,状态码变化能告诉你很多后端处理信息:
- 同一个URL,不带Cookie访问返回302跳到登录页,带了Cookie访问返回200——说明这个页面需要登录才能访问。
- 同一个URL,POST访问返回405或404,但GET访问返回200——说明服务端只处理GET。
- 改参数ID后,从200变成500——大概率是查询数据时出了异常,可能是参数格式问题,也可能是不存在的数据触发了错误。
- 改请求头后从403变成200——说明服务端有请求头校验,放行条件被你试出来了。
这种推理方式是排查问题最核心的能力。状态码只是结果,真正有价值的是“为什么发生这个结果”以及“哪些输入影响了这个结果”。
4.4 状态码速查表:放收藏夹不亏
我做一个精简速查表,把日常最常用的状态码和排查方向列在一起:
| 状态码 | 含义 | 优先排查方向 |
|---|---|---|
| 200 | 成功 | 检查返回内容是否符合预期 |
| 301/302 | 跳转 | 检查Location头指向哪里 |
| 304 | 使用缓存 | 是否需要强刷 |
| 400 | 请求格式错误 | 检查参数格式、JSON语法、Content-Type |
| 401 | 未认证 | 检查认证信息是否缺失或过期 |
| 403 | 禁止访问 | 检查权限、IP、请求头 |
| 404 | 未找到 | 检查URL路径、参数名 |
| 405 | 方法不允许 | 切换GET/POST试试 |
| 429 | 被限流 | 降低请求频率,检查是否触发风控 |
| 500 | 服务器异常 | 看后端日志,多半是代码逻辑问题 |
| 502 | 网关错误 | 检查后端服务是否存活 |
| 503 | 服务不可用 | 确认服务是否在维护/过载 |
| 504 | 网关超时 | 检查后端响应时间、慢查询等 |
5. 常见问题与排查技巧实录
5.1 问题一:502 Bad Gateway到底怎么排查
这几天学习时,我在自己搭的一个本地服务上反复遇到unexpected status 502 bad gateway: unknown error, url: http://127.0.0.1:1572这种报错。这类报错的特点很典型:URL地址是127.0.0.1本地地址,说明请求发往本地某个服务,但网关转发不成功。
我的排查三步走,你可以直接套用:
第一步,确认后端服务有没有启动。本地开发最容易犯的错就是服务没启动就开始请求。用浏览器直接访问这个127.0.0.1:1572,如果页面都打不开,说明后端没活。
第二步,确认端口对不对。服务启动了但端口不对,也会502。检查启动日志里监听的端口和请求URL里的端口是否一致,不一致就改对。
第三步,确认服务是否还在启动过程中。有些服务启动需要几秒,你立刻请求就可能碰到502。等几秒再试,通常就好了。
5.2 问题二:Postman请求失败但浏览器能访问
这个现象很常见,原因通常是以下三个中的一个:
第一,缺请求头。浏览器自动带了Cookie、UA、Referer等,Postman裸奔过去被服务端拒绝。解决方案是在浏览器里复制cURL导到Postman,保留全部请求头。
第二,代理设置问题。如果Postman里设置了系统代理,但本地服务不走代理,请求会失败。检查Postman的Settings里的Proxy设置,把Use the system proxy关掉或添加本地地址到No Proxy列表。
第三,SSL证书问题。访问自签名HTTPS接口时,Postman默认校验证书会失败。在Postman的Settings里把SSL certificate verification关掉(仅限测试环境),或者导入服务端证书。
5.3 问题三:000状态码代表什么
有时候抓包工具里会出现状态码000,Postman里显示请求失败。000不是服务器返回的,而是客户端自己报的——连接都没建成,根本拿不到响应。常见原因包括:
- 目标地址不可达(域名解析失败、端口没开、服务挂了)。
- 被防火墙拦截,连接被重置或丢弃。
- 本地代理配置错误,请求压根没发出去。
- 证书验证失败导致连接终止。
排查时,先ping通不通,再telnet ip port看端口通不通,最后看有没有代理拦截,很快就定位了。
5.4 表格:Postman使用中的常见坑
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 发送后一直转圈卡住 | URL不可达、代理未配置、等待超时 | 换浏览器访问确认URL,检查代理 |
| 服务端报参数缺失 | Body格式选错,参数写错位置 | 确认选JSON时去raw里写JSON文本 |
| 返回乱码 | 编码不一致 | 检查响应显示编码,切换UTF-8 |
| 请求带不了Cookie | 手动模式下Cookie头没设置 | 在Headers里手动添加Cookie |
| 环境切换后请求失败 | 环境变量引用未生效 | 检查{{变量名}}拼写和当前环境选择 |
| 导出的cURL导入报错 | 命令太长或格式不完整 | 确保从浏览器复制的是完整命令 |
| 定时请求不执行 | 版本或权限问题 | 确认使用官方版本,设置正确的工作目录 |
5.5 排查问题的一个方法论:抓包+对比+二分法
这节内容学到后面,我总结出排查HTTP相关问题的三板斧:抓包定位、对比分析、二分缩小。
抓包定位,就是先看请求到底发了什么、服务器到底回了什么,不要猜。对比分析法,是改一个变量,比如加一个请求头,看响应是否变化,判断这个字段的作用。二分法,是在多个请求头都可能是嫌疑时,先去掉一半,看问题是否复现,用来快速缩小范围。
举个例子,接口返回403,你不知道是Cookie的问题、UA的问题还是Referer的问题。先全部去掉请求头,裸请求看响应;然后只加Cookie,不行;于是加上Referer,如果变成200,说明问题就在Referer。这个过程中每一步都是控制变量,结论可靠。学安全调试接口,说白了就是不断练习“观察输入和输出之间的关系”。
6. 写在最后
Day 10这节课我把HTTP数据包、Postman构造、请求方法、请求头修改、状态码判断这几块内容学完后,最大的感受是:HTTP协议真没有那么玄乎,就像人和人之间的对话规则一样,你只要肯动手看包、发包、改包,比看十遍书都管用。
我个人实际操作中的体会,一定不要上来就背请求头列表和状态码表。先用Postman发几十个请求,发到某个接口报403了,再去翻请求头,你就永远记住Referer是干嘛的了;碰到500了,再去看后端日志,你就对500有了肌肉记忆。用需求驱动学习,比死记硬背效率高太多。
最后再分享一个小技巧:给自己布置一个“百包计划”。找五个公开的API接口,每个接口分别用GET、POST、加请求头、带Cookie这几种方式各请求一遍,看完响应后再用浏览器打开同一个地址对比差异。五天下来,HTTP数据包在你眼里就不再是陌生文本了。后面学什么抓包、什么注入、什么越权,都是建立在这个基础上的。基础打得牢,后面才走得远。