适用场景
HTTP 请求方法限制适用于以下场景:等保测评与渗透测试报告中要求关闭 TRACE、PUT、DELETE 等高风险方法;站点仅提供静态内容或常规表单接口,却扫描出支持 WebDAV 的 PROPFIND、MKCOL、MOVE、COPY 方法;Windows + IIS 环境默认启用 WebDAV 模块,存在被上传脚本文件形成 WebShell 的风险;Apache 默认未限制方法,Allow 头暴露了服务端支持的方法清单。这类问题单条风险不高,但在合规检查中属于必查项,且配置成本很低,属于性价比高的加固项。
前置条件
- 具备 Nginx / Apache / IIS 的配置文件修改与重载权限,Windows 环境需管理员权限操作 IIS 模块。
- 已梳理业务真实需要的方法清单:静态站通常只需
GET、HEAD;表单站需要POST;REST 风格接口还需PUT、PATCH、DELETE;若使用 WebSocket 或 gRPC 需额外确认。 - 变更前已完成配置备份,并有一个可回滚的窗口。
- 已确认现有前端、移动端、第三方回调没有依赖被禁用的方法。
原理说明
HTTP 方法本身是协议能力,风险来自「能力被非预期地开放」。常见风险有三类。
第一类是 TRACE 与跨站追踪(XST)。TRACE 会原样回显请求内容,攻击者配合浏览器漏洞与 XSS 可读取带 HttpOnly 标记的 Cookie。现代浏览器已限制 XMLHttpRequest 使用 TRACE,但不代表应该继续开放该方法,合规检查通常直接要求返回 405。
第二类是 WebDAV 写入类方法。当 IIS 的 WebDAV 模块、Apache 的 mod_dav 或 Nginx 第三方 DAV 模块开启时,PUT 可直接上传文件、MKCOL 建目录、MOVE 改名、COPY 复制。若上传目录可被解析器执行(例如 .aspx、.php),就形成完整的 GetShell 链路。这也是历史上一批 IIS 站被挂马的主因。
第三类是 方法探测信息泄露。OPTIONS 请求会返回 Allow 头,把服务端支持的方法、有时甚至是中间件指纹一起暴露,为后续攻击提供输入。
加固思路有两层。边缘层做方法白名单,凡是白名单外的方法统一返回 405,并补上语义正确的 Allow 头;应用层确保即使边缘被绕过,上传与写入能力也因模块被移除而不可用。两层叠加的原因是边缘规则可能被新开的 CDN、备用入口、内网回源路径绕过,而模块卸载属于能力层面的消除。
需要注意的是 limit_except 的语义陷阱:Nginx 中 limit_except GET { deny all; } 会同时允许 HEAD,因为 Nginx 内部把 HEAD 视为 GET 的变体。若业务上必须精确限制,应改用 if ($request_method !~ ^(GET|HEAD)$) { return 405; },并注意 if 在 location 中的使用限制。
操作步骤
步骤 1:Nginx 方法白名单
全局层先做一次粗粒度限制,再对接口路径做精细放行。推荐用 map 而不是 if,既避免 if is evil 的副作用,也便于统一维护。
# /etc/nginx/conf.d/method-restrict.conf
map $request_method $method_ok {
default 0;
GET 1;
HEAD 1;
POST 1;
}
map $request_uri $method_ok_uri {
default $method_ok;
# 静态资源与下载入口只允许 GET / HEAD
"~*^/(static|images|upload|download)/" $method_ok;
}
server {
listen 443 ssl;
server_name www.example.com;
# 全站粗粒度限制:非白名单方法返回 405,并显式给出 Allow 头
if ($method_ok = 0) {
add_header Allow "GET, HEAD, POST" always;
return 405;
}
# 明确拒绝 TRACE(Nginx 默认不实现,但仍需防中间层透传)
if ($request_method = TRACE) {
add_header Allow "GET, HEAD, POST" always;
return 405;
}
# 拒绝 WebDAV 系列方法,避免被探测到写入能力
if ($request_method ~* ^(PROPFIND|PROPPATCH|MKCOL|COPY|MOVE|LOCK|UNLOCK|DELETE|PUT|PATCH)$) {
add_header Allow "GET, HEAD, POST" always;
return 405;
}
}
REST 接口需要写方法时,在更具体的 location 中覆盖限制:
# 仅 API 路径放行 PUT / PATCH / DELETE
location /api/ {
limit_except GET HEAD POST PUT PATCH DELETE {
deny all;
}
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Real-IP $remote_addr;
# 明确清理边缘可能注入的方法相关响应头
proxy_hide_header Allow;
add_header Allow "GET, HEAD, POST, PUT, PATCH, DELETE" always;
}
若站点使用宝塔面板管理,可在「网站 → 设置 → 配置文件」中把上述片段追加到 server 块内,保存后重载。宝塔重载前建议手动执行一次 nginx -t,避免语法错误导致站点不可用。
步骤 2:Apache 方法与 WebDAV 关闭
# 关闭 TRACE
TraceEnable off
<Directory /var/www/html>
Options -Indexes -FollowSymLinks
AllowOverride None
Require all granted
# 只允许 GET / POST / HEAD,其余全部拒绝
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
</Directory>
# 关闭 DAV 能力(mod_dav 未卸载时也确保未启用)
<Location />
Dav off
</Location>
若确认业务不需要 mod_dav,直接从模块列表卸载更彻底:
# Debian / Ubuntu
a2dismod dav dav_fs dav_lock
a2enmod headers
systemctl reload apache2
# RHEL / CentOS
sed -i 's/^LoadModule dav_module/#LoadModule dav_module/' /etc/httpd/conf.modules.d/00-dav.conf
httpd -t && systemctl reload httpd
步骤 3:IIS 与 WebDAV 模块移除
Windows + IIS 环境下,WebDAV 是独立模块,必须显式移除,仅靠 web.config 的限制在部分版本上会被模块绕过。
<!-- web.config:方法白名单 -->
<configuration>
<system.webServer>
<security>
<requestFiltering removeServerHeader="true">
<verbs allowUnlisted="false">
<add verb="GET" allowed="true" />
<add verb="HEAD" allowed="true" />
<add verb="POST" allowed="true" />
</verbs>
<fileExtensions allowUnlisted="false">
<add fileExtension=".html" allowed="true" />
<add fileExtension=".css" allowed="true" />
<add fileExtension=".js" allowed="true" />
<add fileExtension=".png" allowed="true" />
<add fileExtension=".jpg" allowed="true" />
</fileExtensions>
</requestFiltering>
</security>
<modules>
<!-- 移除 WebDAV 模块,从能力层消除 PUT 上传 -->
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
</handlers>
</system.webServer>
</configuration>
若服务器整体不需要 WebDAV,可直接卸载功能并停止相关服务:
# 管理员 PowerShell
Uninstall-WindowsFeature Web-DAV-Publishing
Remove-WebConfigurationProperty -PSPath 'IIS:\' -Filter 'system.webServer/modules' `
-Name '.' -AtElement @{name='WebDAVModule'}
Stop-Service WebClient -Force
Set-Service WebClient -StartupType Disabled
iisreset /noforce
IIS 上还有一个容易忽略的点:requestFiltering 的 verbs 配置只对静态请求生效,动态处理器(如 ASP.NET 的 .aspx)走的是另一条链路,因此 WebDAVModule 的移除不可省略。
步骤 4:边缘 WAF 兜底
云 WAF 或自建 ModSecurity 上增加一条方法白名单规则,覆盖因新开域名、备用端口、内网回源而绕过 Nginx 的路径:
# ModSecurity 方法白名单
SecRule REQUEST_METHOD "!@rx ^(GET|HEAD|POST)$" \
"id:1000801,phase:1,deny,status:405,log,\
msg:'Method not allowed',\
setenv:Allow_405=GET|HEAD|POST"
# 单独拦截 TRACE,日志中标记以便统计探测行为
SecRule REQUEST_METHOD "@streq TRACE" \
"id:1000802,phase:1,deny,status:405,log,msg:'TRACE method blocked'"
在云 WAF 控制台的等效做法是新建自定义规则:匹配条件为请求方法属于 TRACE,PUT,DELETE,PROPFIND,PROPPATCH,MKCOL,MOVE,COPY,LOCK,UNLOCK,动作选择拦截并返回 405。规则上线后先以观察模式运行 24 小时,确认业务无正常请求被拦再切换为拦截模式。
步骤 5:清理方法探测的响应特征
# Nginx:隐藏 Allow 与 Server 相关信息,减少指纹暴露
server_tokens off;
more_set_headers -s 405 "Allow: GET, HEAD, POST";
# OPTIONS 请求直接返回 204 或 405,不再透传到后端
if ($request_method = OPTIONS) {
add_header Allow "GET, HEAD, POST" always;
return 204;
}
配置验证
HOST="https://www.example.com"
# 1. 逐个方法探测,405 为预期结果
for m in GET HEAD POST PUT DELETE PATCH TRACE OPTIONS PROPFIND MKCOL MOVE COPY; do
code=$(curl -s -o /dev/null -w '%{http_code}' -X "$m" "$HOST/")
allow=$(curl -sI -X "$m" "$HOST/" | grep -i '^allow:' | tr -d '\r')
printf '%-10s %s %s\n' "$m" "$code" "$allow"
done
# 2. 反向验证:REST 接口的写方法必须仍然可用
curl -s -o /dev/null -w 'api PUT -> %{http_code}\n' -X PUT "$HOST/api/health" -d '{}'
curl -s -o /dev/null -w 'api PATCH -> %{http_code}\n' -X PATCH "$HOST/api/health" -d '{}'
# 3. 尝试通过 PUT 上传文件,必须失败
curl -s -o /dev/null -w 'put shell -> %{http_code}\n' \
-X PUT "$HOST/uploads/test.aspx" --data 'probe'
# 4. WebDAV 能力探测
curl -sI -X PROPFIND "$HOST/" -H 'Depth: 1' | head -5
# 5. Nmap 方法枚举
nmap -p 443 --script http-methods --script-args http-methods.test-all \
--script-args http-methods.url-path=/ www.example.com
# 6. Nginx 配置语法校验
nginx -t
判定标准:PUT、DELETE、TRACE、PROPFIND 等均应返回 405,且响应中含 Allow 头;/api/ 下的 PUT 与 PATCH 应返回业务预期的状态码(200/204/401 均可,唯独不能是 405);nmap http-methods 的结果中不应出现 WebDAV 方法。
日志侧配合统计被拦方法,判断是否存在持续探测:
awk '$9 == 405' /var/log/nginx/access.log | awk '{print $6, $7}' \
| sort | uniq -c | sort -rn | head -20
# IIS 日志中统计被拒方法(W3C 格式,cs-method 为第 4 列)
awk '{print $4}' /var/log/iis/W3SVC1/*.log | sort | uniq -c | sort -rn | head
常见问题
FAQ 1:加了方法限制后表单提交或接口返回 405,如何快速定位是哪里配错了?
先确认请求实际使用的方法,而不是猜测。用 curl -v 或浏览器开发者工具的 Network 面板查看 Request Method;常见意外包括表单被前端改写为 PATCH、跨域请求触发 OPTIONS 预检、旧版前端库把 DELETE 伪装成 POST 但携带 X-HTTP-Method-Override 头。确认方法后,检查三层配置的生效顺序:Nginx 的 if ($method_ok = 0) 若写在 server 块中,其执行时机早于 location 内的 limit_except,会导致 /api/ 的放行失效。解决办法是把粗粒度限制收敛为只拦「明确的高危方法」(TRACE、PROPFIND 等),把方法白名单下移到具体 location。另外,CDN 或云 WAF 若也配置了方法白名单,需三段配置保持一致的允许集合,否则会出现「源站放行、边缘拦截」的现象,排查时应先用源站 IP 加 hosts 绑定直连验证。
FAQ 2:Nginx 返回 405 时没有带 Allow 头,是否会影响合规检查与搜索引擎?
按 RFC 9110,405 响应必须包含 Allow 头声明该资源支持的方法,而 Nginx 的 return 405 默认不生成该头。补法是使用 add_header Allow "GET, HEAD, POST" always;,其中 always 关键字保证在非 2xx 状态下也输出,这是最容易被漏掉的参数。搜索引擎侧对 405 的处置是忽略该 URL,不会误判为站点故障;但需要注意不要把 405 错误页配置为返回 200(某些「友好错误页」方案会这样做),否则会让爬虫把无效方法请求当作正常内容抓取。若担心误伤,可在 405 页面上返回一段简短说明并保留正确的状态码,检查方式是 curl -sI -X TRACE https://站点/,确认状态行是 405 且 Allow 头存在。
FAQ 3:只用防火墙或 WAF 拦截,不改服务器配置,可行吗?
可以作为过渡手段,但不能作为最终方案。原因是边缘拦截只覆盖经过边缘的流量,同机房内网请求、回源直连、新解析但未接入 WAF 的子域、临时开放的测试端口都会绕过。只要 IIS 的 WebDAV 模块仍处于启用状态,攻击者一旦找到一个未接入边缘的入口,PUT 上传的链路依然成立。因此推荐顺序是:先在服务器侧从能力上移除写入方法(卸载模块、Dav off),再用边缘规则对历史入口与未知入口做兜底,最后把方法限制写入安全基线检查项,纳入后续配置漂移巡检。
总结
HTTP 方法加固分两步:先明确业务真正需要的方法集合,再在服务器层把它变成白名单。Nginx 侧推荐用 if ($request_method !~ ...) 拦高危方法、在接口 location 内用 limit_except 精确放行,并注意 limit_except GET 会连带允许 HEAD;Apache 侧关闭 TraceEnable、用 LimitExcept 限制目录方法、把 Dav 设为 off 或直接卸载模块;IIS 侧必须移除 WebDAVModule,不能只依赖 requestFiltering。
验证环节要同时覆盖正反两面:高危方法必须返回 405 并带 Allow 头,业务需要的写方法必须仍然可用。最后用 nmap http-methods 与访问日志统计做一次外部复核,确认方法探测面已经收敛,再把这套配置固化进基线模板,避免下次重装或迁移时又被默认配置打开。