阅读指引
本文尝试探索以下几个内容:
- ①确认问题真实性: 有支持options请求吗,什情况下会产生options请求?
- ②问题成立的合理性: 建议禁用options请求,并将允许options请求列为安全漏洞的合理依据是什么?
- ③分析问题-产生的原因: 什情况下会产生options请求。
- ④分析问题-跨平台差异: Dcloud 5+app的跨平台项目,而iOS端实测发现会发起大量options请求,公众号网页版也会,但是安卓却不会。
- ⑤解决问题: 如何应对这个所谓“安全漏洞”?options请求的数量,可以缓存优化吗?
一、背景说明
年度渗透测试,测出项目支持options请求,在整改书中将支持options请求列为低位级别安全漏洞,并建议“禁用options请求,仅使用get/post请求”。安全质控部门关于这个低位漏洞的描述是,“0PTIOHS方法可以提供web服务器支持的方法列表,它表示请求有关Reguest uI标识的请求/响应链,可用的通信选项的信息”。
要解决问题,首先要确认问题是否真实存在,然后去分析问题,最后才是解决问题。
以下是我们拿到的安全质控部门发来的原文,分为3个方面:
①漏洞描述:“0PTIOHS方法可以提供web服务器支持的方法列表,它表示请求有关Reguest uI标识的请求/响应链,可用的通信选项的信息”。
②处理建议:“禁用options请求,仅使用get/post请求”。
③漏洞级别:低危漏洞。
让情况变得更复杂的是:项目框架是Dclond 5+app的跨平台项目,项目并没有显性的使用options请求,而iOS端实测发现会发起大量options请求,公众号网页版也会,但是安卓却不会。
软件工程师对于options请求自然是不陌生的,然而将其定位安全漏洞,尽管是低危,也令我感到有些疑惑。
因为在印象中,options请求是预检请求,是 HTTP/CORS 机制的一部分。而HTTP/CORS机制本身就是一种为安全而生的机制。
所以:
①options请求被列为安全漏洞合理吗?依据在哪?
②同一套前端axios请求,为什么安卓webview不产生options请求,web端和iOS端却会?
③这么多options请求日志是哪里来的?get请求也会产生吗?
④options请求的数量,可以通过优化手段减少吗?
二、探索目标
基于这样的疑惑,尝试去明确以下内容:
- ①确认问题真实性: 有支持options请求吗,什情况下会产生options请求?
- ②问题成立的合理性: 建议禁用options请求,并将允许options请求列为安全漏洞的合理依据是什么?
- ③分析问题-产生的原因: 什情况下会产生options请求。
- ④分析问题-跨平台差异: Dcloud 5+app的跨平台项目,而iOS端实测发现会发起大量options请请求,公众号网页版也会,但是安卓却不会。
- ⑤解决问题: 如何应对这个所谓“安全漏洞”
三、确认问题真实性:有支持options请求吗,什情况下会产生options请求?
查看了服务器配置,确实开放了options请求。此外,后台日志也显示有许多options请求,而值得一提的是安卓端没有发现发起options请求,iOS端和公众号网页端都发现有options请求,而且量还不小。
仅是options请求量较大,就可以确认这一问题必须要整改了。由此问题得到确认,接下来要分析问题。
四、建议禁用options请求,并将允许options请求列为安全漏洞的合理依据是什么?
1.漏洞分类:CWE-200:信息泄露
OPTIONS 请求的响应会返回服务器支持的 HTTP 方法(Allow
头)和 CORS 策略(如 Access-Control-Allow-Methods
、Access-Control-Allow-Headers
)。攻击者可借此探测服务器能力,策划针对性攻击(如尝试 PUT/DELETE 方法或利用允许的敏感头)。
另外,通过 OPTIONS 响应的特征(如支持的 HTTP 方法、头字段、服务器类型),攻击者可推断服务器版本或框架,利用已知漏洞。
2.防止资源滥用
- DDoS 攻击媒介
OPTIONS 请求无需携带有效负载,攻击者可高频发送此类请求,消耗服务器资源(尤其未限制频率或缓存时)。 - 冗余网络流量
每个非简单请求(如带自定义头的 POST)均需先发送 OPTIONS 预检请求,增加网络开销。
3.最小化攻击面原则
根据安全最佳实践,应禁用不必要的 HTTP 方法。若业务无需跨域交互,关闭 OPTIONS 请求符合“默认拒绝”安全策略。
以上几点,大概就是安全部门建议禁用options请求的依据。
五、分析问题-产生的原因:什情况下会产生options请求?
但是我们的日常http请求,并没有显性的使用到 options 请求。那么这么多的options请求日志是怎么产生的呢?
options请求发生的场景: 资料显示,OPTIONS 请求是 HTTP/CORS 机制的一部分,用于在浏览器实际发送可能修改服务器状态的请求之前,检查这些请求是否被服务器允许。尽管 GET 请求通常不触发 OPTIONS 请求,但在跨域且涉及特殊请求头的场景中,这种情况可能发生。
我们实测发现,在跨域的测试环境中,post 和 get 请求都会伴随options请求的发生,且在公众号网页端和iOS端都会,唯独安卓端不会。
OPTIONS 请求是 HTTP/CORS 机制的一部分,我们由此推测根本原因有跨域了。
post请求伴随options预检请求,好理解,而 get 也伴随,说明当时的get请求被识别为了“非简单请求”,而比较可能的原因是携带了特殊请求头。
六、分析问题-跨平台差异。
在前面的核实问题真实性中,发现Dcloud 5+app的跨平台项目,而iOS端实测发现会发起大量options请请求,公众号网页版也会,但是安卓却不会。
我们要更好的想解决问题,这点得先搞清楚。
首先,网页版(域名不同)和iOS版本(协议不同)好理解,跨域了而且携带了自定义请求头,前后端双方都配置相关跨域支持,产生options预检请求就显得常规了。
而安卓端(协议不同)也会跨域,但却没发起options请求。如果当时三端都出现options预检请求,我想我不会继续探索跨端差异问题。
然后去查阅了资料,看了安卓和iOS的代码,发现安卓webview是可以绕过(危险操作)options预检请求的。
怎么绕过呢?webview 的参数设置提供了一个方法叫 setAllowUniversalAccessFromFileURLs(true)
,有点类似在早期版本的 chrome 浏览器的跨域设置 --disable-web-security --user-data-dir=C:\MyChromeDevUserData
和 --disable-web-security
的效果。
// 获取 WebView 的配置对象,用于设置各项参数
WebSettings ws = webView.getSettings();
// 允许 file 协议页面(如本地 HTML)跨域访问其他域资源
// 警告:生产环境慎用!存在跨域安全风险(CVE-2019-5768)
ws.setAllowUniversalAccessFromFileURLs(true);
// 允许 WebView 访问本地文件(file:// 协议)
// 风险:可能泄露设备文件(建议限制作用域)
ws.setAllowFileAccess(true);
当然,iOS 的 webview 也有类似的配置。
所以跨端产生的关于options请求表现差异,是因为 webview 可以设置,允许 file:// 协议页面访问任意域(http/https)(有风险的操作)。
七、解决问题:如何应对这个所谓“安全漏洞”
最后解决问题。
问题是安全部门提出的,他们认为允许options请求有安全风险,建议禁止。
# 在 Apache 主配置或虚拟主机中,全局禁用
# 允许 GET 和 POST,禁止其他方法
<LimitExcept GET POST>
Require all denied
</LimitExcept>
# 如果需要仅在某个路径下限制方法(如 `/api/`):
<Location "/api/">
<LimitExcept GET POST>
Require all denied
</LimitExcept>
</Location>
# 全局禁用非 GET/POST 方法
server {
listen 80;
server_name example.com;
# 全局拦截非 GET/POST 请求
limit_except GET POST {
deny all;
}
# 其他配置...
}
# 针对特定路径的配置
server {
listen 80;
server_name example.com;
location /api/ {
# 仅允许 GET/POST,其他方法返回 403
limit_except GET POST {
deny all;
}
# 其他配置(如代理或静态文件)
proxy_pass http://backend;
}
}
所以最简单的就是在服务端和web应用服务器,直接禁止options请求。
web 端原本可以采用反向代理,然后禁用options请求,解决问题。但现实是,这是一套代码生成多端的跨端设计,而且共用同一套服务接口。禁用options请求,在安卓端和iOS端会导致请求无法正常进行,因为这边是跨域(file:// 协议本地html 和 htpps 请求接口,协议不同)的。
所以如果要禁用options请求,有一种方案是将在网页端发起的axios请求,改为使用原生的OkHttp
作为 HTTP 客户端。但这会增加大量工作量,而且违背一套代码生成多端的初衷,所以这不适用。
那么接下来,考虑采取的措施:
- 精细化 CORS 配置,限定更细致的允许跨域范围,降低信息泄露和跨域攻击风险。
- 执行预检请求缓存优化策略,减少 OPTIONS 请求频率。
# 要通过 **预检请求缓存优化** 来减少 OPTIONS 请求频率,
# 核心是配置 `Access-Control-Max-Age` 响应头,
# 告知浏览器缓存预检结果的有效期。
<IfModule mod_headers.c>
# 处理 OPTIONS 预检请求
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
# 设置 CORS 头
Header set Access-Control-Allow-Origin "https://your-domain.com"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
Header set Access-Control-Max-Age "600" // 缓存 10 分钟
</IfModule>
# 注意事项:
# ① 所有现代浏览器均支持 `Access-Control-Max-Age`,
# 但需注意,IE ≤ 11,最大缓存时间固定为 600 秒。
# ② Access-Control-Max-Age 会缓存 CORS 策略,
# 这意味着更改缓存策略可能不会马上生效,当然这通常影响不大。
# 值得一提的是 Access-Control-Max-Age 不会导致数据旧化,
# 常规 GET/POST 的实际响应数据,由 Cache-Control 控制。
写在最后
小结:
① OPTIONS 请求是 HTTP/CORS 机制的一部分,只有跨域场景才会发生 options 请求。
② get 请求也可能会伴随 options 预检请求(例如自定义特殊请求头)。
③ webview 中也会像常规浏览器那样执行 CORS 机制。
④ webview 提供了方法允许跨域访问其他域资源(生产环境慎用)。
⑤ 减少 OPTIONS 请求频率,可以在应用服务器侧,执行预检请求缓存优化策略。
⑥ 降低信息泄露和跨域攻击风险,可以通过精细化 CORS 配置,限定更细致的允许跨域范围实现。
⑦ 关于缓存,值得一提的是 Access-Control-Max-Age 不会导致数据旧化,其缓存的是 CORS 策略,常规 GET/POST 的实际响应数据缓存策略,由 Cache-Control 控制。
⑧ 解决 webview 加载本地html和http请求跨域的方案之一是:将在网页端发起的axios请求,改为使用原生的
OkHttp
作为 HTTP 客户端。⑨ options 请求被列为安全漏洞的可能原因:信息泄露(CWE-200)、防止资源滥用、最小化攻击面原则。
未尽事宜: 对于Dclond 5+app的跨平台项目,有没有办法不跨域呢?
(主要是指安卓和iOS端本地加载html资源用file:// 协议,而接口则是 http 协议跨域场景。如果给html搭建一个和接口同源的服务器,流畅度会下降,如果改为使用原生端的http发请求,改造量大而且和其他端的请求就不是同一套代码了,虽然webview有允许跨域的配置但那本身就是风险操作。)