导语:HPP(HTTP Parameter Pollution,HTTP 参数污染)不是什么高深漏洞,它的本质是「同一参数名出现多次,不同组件对它的处理不一致」。但正是这种不一致,让一个普通的
role=user&role=admin直接拿到管理员权限。
一、HPP 是什么
HPP 的概念由 Stefano Di Paola(斯泰法诺·迪保拉)和 Luca Carettoni(卢卡·卡雷托尼)在 2009 年 OWASP AppSec Europe(OWASP 欧洲应用安全大会)首次提出。核心思路很简单:HTTP 请求里同一个参数出现两次或多次,WAF、代理、框架、业务代码各层对这些重复值的处理方式可能完全不同。
This can be found at places where HTTP Parameters are sent such as: 1. GET Requests 2. POST Requests 3. Cookies
参数源不只 URL Query String,POST Body、Cookie、Header 都可能成为污染载体。攻击者通过污染这些位置,让某一层「看到」的是合法值,另一层「执行」的却是恶意值。

二、4 种解析模式 + 6 类后端栈行为
不同技术栈面对 id=1&id=2 的反应是不一样的,Acunetix 2024 整理的简化版如下:
| 技术 / 层 | 重复参数行为 | 安全风险 |
|---|---|---|
| PHP / Apache | 取最后一个值(标量读取时) | 校验与执行层可能错位 |
| ASP.NET / IIS | 拼接为 1,2 或数组 | 业务逻辑与校验期望不一致 |
| Java Servlets(Java 服务端小程序) | 取第一个值或数组 | 安全检查可能没覆盖执行路径 |
| Node.js / Express | 框架相关(req.query 默认第一个,body-parser 看配置) | 客户端与服务端解析错位 |
| Python / Flask / Django | 取第一个值(request.GET.get) | 列表化读取会拿到全部 |
| 反向代理 / WAF / CDN | 各家实现差异大 | 校验层与执行层不在同一个上下文里 |
更抽象地看,重复参数在栈里的处理模式可以归为 4 类:
- First-wins(取第一个):Apache、PHP、Tomcat 默认
- Last-wins(取最后一个):ASP.NET、IIS 部分场景
- Concatenation(拼接):变成
1,2这种字符串 - Array(数组):所有值都保留,按索引访问
id=1&id=2 这条请求在不同栈里就是 4 种语义,这就是 HPP 能打的根因。
三、客户端 HPP vs 服务端 HPP
Acunetix 把 HPP 拆成两类,区分清楚才能定测试方法:
Client-side HPP(客户端 HPP):浏览器或前端 JS 读 URL 参数渲染页面时,前端逻辑按一种规则解析,服务端按另一种规则解析。攻击者构造一段 URL,受害者点开,前端校验通过、业务流程正确,提交到服务端时却变成另一种语义。
Server-side HPP(服务端 HPP):请求进入后端栈,反向代理、WAF、框架、业务代码各取所需。role=user&role=admin 经典越权就是这个模式——WAF 看到 user 放行,业务代码读到 admin 执行。
Server-side HPP is generally more severe because it directly affects how the application processes data.
服务端 HPP 直接影响数据处理链路,危害面比客户端 HPP 大得多。
四、实战四步法
OWASP WSTG-INPV-04(Web 安全测试指南输入验证章节)给的 HPP 测试流程,可以浓缩成四步:
- 识别入口点:所有接收参数的位置(URL / Body / Cookie / Header / JSON / XML)都列出来
- 推断后端栈:响应头、X-Powered-By、错误页、Stack Trace 暴露的技术栈决定了你面对哪种解析模式
- 构造重参数:每个入口点都试
key=victim&key=evil、JSON 里{"key": ["victim", "evil"]}、分号/逗号分隔key=victim;evil - 观察行为差异:响应体内容、数据库写入值、跳转目标、错误信息,跟正常请求对比
GET /api/user?role=user&role=admin HTTP/1.1
Host: target.com
Cookie: session=victim_sess
→ 如果响应里出现管理员面板内容,说明后端取了最后一个 role
五、真实案例
Twitter 2009 状态注入:Di Paola 当年公开的案例之一,构造 status=hello&status=goodbye 让 Twitter 状态更新出现非预期内容,触发链是前端回显取第一个、服务端持久化取最后一个。
Shopify 金额覆盖:HackerOne #106024 报告,通过在 POST Body 里加 amount=1&amount=9999,让某些内部接口取了攻击者控制的高金额值。
HackerOne #105953:在 GraphQL 接口里通过重复字段覆盖权限标志。
HackerOne #298265:OAuth 回调参数被重参数污染,导致重定向到攻击者控制的域名。
这些案例的共性:单层校验 + 多层执行的架构,给 HPP 留足了空间。
六、Python 探测 PoC
import requests
target = "https://target.com/api/profile"
cookies = {"session": "victim_sess"}
# 探测后端栈解析模式
tests = [
{"id": "1", "id": "2"}, # 重复 query
{"role": "user", "role": "admin"}, # 越权探测
{"amount": "1", "amount": "9999"}, # 业务逻辑探测
{"redirect": "/home", "redirect": "https://evil.com"}, # 开放重定向
]
for params in tests:
r = requests.get(target, params=params, cookies=cookies)
print(f"PAYLOAD: {params}")
print(f"STATUS: {r.status_code} | LEN: {len(r.text)} | TITLE: {r.text[:80]}")
print("---")
关键观察点:
- 响应长度突变(说明取到了不同的值)
- 出现管理员专属字符串(”admin”、”dashboard”)
- 重定向 Location 头指向了攻击者域名
- 数据库写入值跟请求 Body 最后一个一致
七、防御四要点
HPP 没有银弹,但下列四条能堵住绝大多数攻击面:
- 白名单参数访问:业务代码只读明确需要的参数名,不依赖底层框架的「取最后一个」默认行为
- URL/Body 标准化:网关层对重复参数做强制合并策略,全栈保持一致
- WAF 规则覆盖重复值:WAF 不只检查单参数,对
key=a&key=b这种模式要按攻击向量处理 - 多层一致性审计:WAF、框架、业务代码各层分别审计,确保它们对同一请求的解析结果一致
OWASP WSTG 把 HPP 归类为 Input Validation Testing(输入验证测试)项,不是没原因的——它本质就是「输入在多层的解释不一致」。
八、思考题
- 同一个
id=1&id=2请求,PHP、ASP.NET、Python Flask 分别会怎么处理?哪个最容易出安全问题? - 如果目标站有 Cloudflare 在前面,业务代码是 Node.js Express,HPP 攻击面会出现在哪一层?
- JSON Body 里
{"role": ["user", "admin"]}算 HPP 吗?跟 Query String 的role=user&role=admin本质区别是什么?
素材出处
- harsh-bothra/learn365 day19「HTTP Parameter Pollution」原文
- Acunetix《HTTP Parameter Pollution (HPP)? Examples, Prevention, API Risks》
- OWASP WSTG-INPV-04 Testing for HTTP Parameter Pollution
- Stefano Di Paola & Luca Carettoni《HTTP Parameter Pollution》AppSecEU09(OWASP 2009 大会)
- HackerOne 报告 #105953、#298265、#106024














暂无评论内容