HTTP 参数污染 HPP:重复参数的解析裂缝与红队实战

导语: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 都可能成为污染载体。攻击者通过污染这些位置,让某一层「看到」的是合法值,另一层「执行」的却是恶意值。

HPP 概念图

二、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 测试流程,可以浓缩成四步:

  1. 识别入口点:所有接收参数的位置(URL / Body / Cookie / Header / JSON / XML)都列出来
  2. 推断后端栈:响应头、X-Powered-By、错误页、Stack Trace 暴露的技术栈决定了你面对哪种解析模式
  3. 构造重参数:每个入口点都试 key=victim&key=evil、JSON 里 {"key": ["victim", "evil"]}、分号/逗号分隔 key=victim;evil
  4. 观察行为差异:响应体内容、数据库写入值、跳转目标、错误信息,跟正常请求对比
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(输入验证测试)项,不是没原因的——它本质就是「输入在多层的解释不一致」。


八、思考题

  1. 同一个 id=1&id=2 请求,PHP、ASP.NET、Python Flask 分别会怎么处理?哪个最容易出安全问题?
  2. 如果目标站有 Cloudflare 在前面,业务代码是 Node.js Express,HPP 攻击面会出现在哪一层?
  3. 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

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容