Postman需切raw模式手动构造multipart请求,自定义boundary和Content-Disposition中的filename(如"test.jpg"),嵌入PDF等真实文件二进制内容,绕过form-data自动填充限制以测试服务端是否仅校验后缀而忽略魔数。

Postman 怎么构造 filename 与实际内容不一致的上传请求
Postman 默认拖入文件时,会自动从文件系统读取真实扩展名并填入 Content-Disposition 的 filename 字段,比如选中 report.pdf,它就会发 filename="report.pdf" —— 这无法触发“后缀与内容不一致”的校验场景。要测服务端是否只看后缀、忽略魔数(file signature),必须手动绕过这个自动行为。
实操方法只有一个:不用 form-data 模式,改用 raw + multipart/form-data 手动构造请求体。关键点在于自己写 boundary、自己拼 Content-Disposition,把 filename 改成目标后缀(如 "avatar.jpg"),但实际塞入 PDF 文件的二进制内容。
- 先用十六进制编辑器或
xxd确认 PDF 文件开头是%PDF-1.(魔数),确保你拿的是真 PDF - 在 Postman 中 Body →
raw,Content-Type Header 设为multipart/form-data; boundary=----WebKitFormBoundaryabc123 - raw 内容按 RFC 7578 格式手写,其中
filename="test.jpg",但紧接着的二进制块粘贴的是 PDF 文件的 base64 解码后字节(可用 Pythonbase64.b64decode()提前转好) - 务必保留每段之间的空行和结尾的
--boundary--,少一个换行就可能被解析为文本字段而非文件
为什么 Content-Type 字段不能信,得看魔数
很多服务端只检查 Content-Type Header(如 image/jpeg)或 filename 后缀,却跳过对文件头几个字节(即魔数)的校验。比如传一个 PDF 文件但设 Content-Type: image/png,再配 filename="map.png",Spring Boot 的 MultipartFile.getContentType() 返回的就是伪造的 image/png,而非真实类型。
真正可靠的校验方式是读取文件前 N 字节做魔数比对:ffd8ff 是 JPEG,89504e47 是 PNG,25504446 是 PDF。Postman 本身不提供魔数注入功能,所以测试时你要主动提供“假头真内容”的组合,观察服务端返回是 200 还是 415/400。
- 常见翻车点:前端 JS 用
File.type设置Content-Type,但该字段完全由浏览器根据扩展名推测,不可信 - 后端若只依赖
request.getFile().getContentType()做白名单拦截,等同于没校验 - Postman 的 form-data 模式会覆盖你手动填的
Content-Type字段,所以必须切到raw模式才能控制它
测试压缩包嵌套脚本或畸形结构的上传请求
构造恶意 ZIP 文件(如内含 .php 或 .js)并伪装成图片上传,是典型的混合内容攻击测试点。Postman 无法直接“修改 ZIP 内部结构”,但可以配合外部工具完成链路:
Postman 12.27.7 Windows x64 官方安装包,适用于 API 设计、调试、测试与协作。该系列包含 Enterprise 云端性能测试静态 IP 出口能力,以及接口规范标签管理改进,适合受限网络测试与规范资源组织场景。
- 用
zip命令行或 7-Zip 创建一个正常 ZIP,再用echo '' > shell.php && zip -u exploit.zip shell.php注入 PHP 文件 - 将该 ZIP 重命名为
photo.jpg,但不要用简单改后缀的方式——用dd在开头插入 JPEG SOI 标记(ffd8),使其同时满足 JPEG 魔数和 ZIP 可解压性(即“双扩展名”文件) - 在 Postman raw 模式中,以
filename="photo.jpg"发送该文件,并监控服务端是否解压、是否保存了shell.php - 重点观察响应状态码和响应体:若返回 200 且后续能访问到
/uploads/shell.php,说明服务端未做内容安全扫描或后缀白名单校验失效
容易被忽略的边界:服务端对 filename 的编码与截断处理
当 filename 包含 Unicode 字符(如中文、emoji)或超长路径时,不同框架解析行为差异极大。例如 Spring Boot 5.3+ 默认使用 ISO-8859-1 解码 filename,而浏览器可能用 UTF-8 编码;Django 则默认信任 filename* 字段。Postman 的 form-data 模式会自动做 URL 编码,但 raw 模式下你得自己处理。
最常踩的坑是:你填了 filename="测试_头像.png",服务端收到却变成 "??_?.png",导致后续文件保存失败或路径遍历漏洞被绕过。这时要检查三处:
- Postman 是否勾选了
Encode value(form-data 模式下该选项影响 filename 编码) - 服务端是否配置了
spring.servlet.encoding.force-request=true强制请求体编码 - 反向代理(如 Nginx)是否截断了过长的
Content-Disposition头,尤其当 filename 超过 255 字节时
复杂点在于,同一个请求,在 Chrome DevTools 里看到的 Content-Disposition 和 Postman raw 模式里手写的,字节序列可能差一个 0x00 或换行,而某些解析库(如 Apache Commons FileUpload)对空白字符极其敏感——差一个 CR/LF 就整个 part 解析失败。










