文章总结: CVE-2026-54665是ApacheNiFi中的预认证主机头注入漏洞,攻击者可通过X-Forwarded-Host或X-ProxyHost头操控OAuth2重定向URI等关键参数。该漏洞源于2025年重构时误删主机允许列表验证机制,影响0.0.1至2.9.0版本,在启用OIDC时可能导致账户接管(CVSS8.1)。官方已在2.10.0版本通过NIFI-15953修复,建议用户立即升级并验证代理配置。 综合评分: 85 文章分类: 漏洞分析,WEB安全,应用安全,云安全,威胁情报
CVE-2026-54665:Apache NiFi 中的预认证主机头注入漏洞
Ots安全
2026年6月21日 11:55 广东
在小说阅读器读本章
去阅读
威胁简报
恶意软件
漏洞攻击
CVE-2026-54665:Apache NiFi 中的预认证主机头注入漏洞
发布日期:2026-06-20报告日期:2026-05-15严重性:5.4 中等(基线)/ 8.1 高(使用 OIDC)状态:已在 NiFi 2.10.0 中修复CWE:CWE-444(HTTP 请求解析不一致)+ CWE-601(开放重定向)
Apache NiFi 中存在预认证主机头注入漏洞。任何未经认证的客户端都可以发送 <host>X-Forwarded-Host或 X-ProxyHost<host> 值,并且该值会被 NiFi 为 OAuth2 重定向、SAML SP 实体、CSRF cookie 域和公共端点构建的 URI 所反映出来/nifi-api/authentication/configuration。受影响版本:0.0.1 至 2.9.0。已在 2.10.0 中修复。
有趣的地方不在于这个 bug 本身,而在于它是如何产生的。2025 年的一次重构(NIFI-14209)删除了HostHeaderHandler之前强制执行已记录nifi.web.proxy.host十年的允许列表的组件,并用一个仅验证端口的自定义程序取而代之。重构后,允许列表仍然出现在文档和配置中,但在运行时,没有任何程序会读取主机部分。
漏洞
nifi-commons/nifi-web-servlet-shared/src/main/java/org/apache/nifi/web/servlet/shared/StandardRequestUriProvider.java:97-115:
private String getHost(final HttpServletRequest request) { final String host; final String serverName = request.getServerName(); final String headerHost = getFirstHeader(request, ProxyHeader.PROXY_HOST, // X-ProxyHost ProxyHeader.FORWARDED_HOST, // X-Forwarded-Host ProxyHeader.HOST // Host ); if (headerHost == null) { host = serverName; } else { final Matcher matcher = HOST_PATTERN.matcher(headerHost); if (matcher.matches()) { host = matcher.group(FIRST_GROUP); } else { host = serverName; } } return host;}
匹配模式为^([^:]+):?([1-9][0-9]{2,4})?$:。它接受主机部分不包含冒号的任何字符串。例如attacker.example.com:,,nifi-prod.example.com.evil-attacker.net。127.0.0.1全部匹配。没有允许列表检查。
对比一下同一文件getPath()往下几行的内容:它会对 <value> X-ProxyContextPath、 <value> X-Forwarded-Context 和 <value> 进行验证,并在值不在允许列表中时抛出异常。主机检查只是忘记执行同样的操作了。X-Forwarded-Prefixnifi.web.proxy.context.pathIllegalArgumentException
不验证主机的允许列表
NiFiProperties.java:1603-1614将属性解析nifi.web.proxy.host成列表:
public List<String> getAllowedHostsAsList() { String rawProperty = getProperty(WEB_PROXY_HOST, ""); List<String> hosts = Arrays.asList(rawProperty.split(",")); return hosts.stream() .map(this::normalizeHost) .filter(host -> !StringUtils.isBlank(host)) .collect(Collectors.toList());}
唯一的调用者是FrameworkServerConnectorFactory.getValidPorts(),它仅从每个条目中提取端口,以构建 Jetty 监听器接受的端口集:
private static Set<Integer> getValidPorts(final NiFiProperties properties) { final Set<Integer> validPorts = new HashSet<>(); final int serverPort = getPort(properties); validPorts.add(serverPort); final List<String> allowedHosts = properties.getAllowedHostsAsList(); for (final String allowedHost : allowedHosts) { final Matcher portMatcher = HOST_PORT_PATTERN.matcher(allowedHost); if (portMatcher.matches()) { final String portGroup = portMatcher.group(PORT_GROUP); final int allowedPort = Integer.parseInt(portGroup); validPorts.add(allowedPort); } } return validPorts;}
因此,如果操作员阅读了管理指南中关于主机配置的部分nifi.web.proxy.host,将其设置为某个值nifi.example.com,nifi-internal.example.com,并认为 NiFi 会拒绝其他主机值的请求,那就错了。NiFi 仅使用这些配置来了解哪些端口是有效的。
概念验证
服务器:apache-nifi-2.9.0-bin.zip以默认设置启动conf/nifi.properties(仅nifi.web.https.port=18443更改以释放默认端口)。单用户模式,HTTPS 已启用127.0.0.1:18443。
基线:
$ curl -sk --resolve localhost:18443:127.0.0.1 -H 'Accept: application/json' \ https://localhost:18443/nifi-api/authentication/configuration{"authenticationConfiguration":{"externalLoginRequired":false,"loginSupported":true, "loginUri":"https://localhost:18443/nf/#/login", "logoutUri":"https://localhost:18443/nifi-api/access/logout/complete"}}
和X-Forwarded-Host:
$ curl -sk --resolve localhost:18443:127.0.0.1 -H 'Accept: application/json' \ -H 'X-Forwarded-Host: attacker.example.com' \ https://localhost:18443/nifi-api/authentication/configuration{"authenticationConfiguration":{"externalLoginRequired":false,"loginSupported":true, "loginUri":"https://attacker.example.com:18443/nf/#/login", "logoutUri":"https://attacker.example.com:18443/nifi-api/access/logout/complete"}}
和X-ProxyHost:
$ curl -sk --resolve localhost:18443:127.0.0.1 -H 'Accept: application/json' \ -H 'X-ProxyHost: evil.example.com' \ https://localhost:18443/nifi-api/authentication/configuration{"authenticationConfiguration":{"externalLoginRequired":false,"loginSupported":true, "loginUri":"https://evil.example.com:18443/nf/#/login", "logoutUri":"https://evil.example.com:18443/nifi-api/access/logout/complete"}}
值得注意的是:TLS 层 Host 头部得到了正确的控制。即使请求的Host: attacker.example.comSNI 匹配,也会被拒绝,Error 400 Invalid SNI因为 JettySecureRequestCustomizer强制执行了 SNI 对抗 Host 的机制。这种防御机制涵盖了 TLS 层 Host 头部。但它并不涵盖上面提到的两个 HTTP 应用层头部,而这两个头部正是 NiFi 前端的反向代理会从不受信任的客户端转发的头部。
下游
RequestUriBuilder.fromHttpServletRequest()在 NiFi 框架的 26 个站点中被调用。反射主机会遍历所有这些站点。安全关键型站点包括:
请求网站 它所构建的StandardOAuth2AuthorizationRequestResolver.java:97 redirect_uri已向 OIDC 身份提供商发送OAuth2请求StandardRelyingPartyRegistrationResolver.java:114 AuthnRequest 中的 SAML SP 实体和 ACS URIOidcAuthenticationSuccessHandler.java:102, 105 OIDC 重定向目标Saml2AuthenticationSuccessHandler.java:103, 106 SAML重定向目标OidcLogoutSuccessHandler.java:213 OIDC 注销后重定向目标StandardCookieCsrfTokenRepository.java:71 CSRF cookie URI,用于推导 cookie 域AuthenticationResource.java:99 匿名/nifi-api/authentication/configuration回复FlowResource.java:398, 2621 内容查看器 URIApplicationResource.java:173, 187, 329 用于集群复制的请求 URI
已验证的影响是匿名/authentication/configuration反射。值得关注的是 OAuth2 redirect_uri。对于启用了 OIDC 的部署,redirect_uri发送给身份提供商 (IdP) 的数据来自攻击者可控制的主机。如果 IdP 接受未注册的重定向 URI,或者对已注册的 URI 不进行通配符匹配(这种情况比您想象的要常见),攻击者就可以控制授权码的发送位置。这就是 5.4 版本中等影响的反射漏洞和 8.1 版本高影响的账户接管漏洞之间的区别。
它是如何来到这里的:NIFI-14209
提交ae5a77b84f5c7e5e51e85e99f1d40079dbdee5f1于 2025 年 2 月 5 日,标题为“重构主机头验证”。提交信息如下:
将 HostHeaderHandler 替换为 HostPortValidatorCustomizer。Jetty SecureRequestCustomizer 强制执行使用服务器证书 DNS 主题备用名称 (SNI) 的主机验证。添加了针对无效主机和端口值的 TLS SNI 的测试。重构并简化了 RequestUriBuilder.fromHttpServletRequest() 方法。
此次删除的差异代码HostHeaderHandler.java共 318 行,是 JettyHandler之前用于处理nifi.web.proxy.host列表并拒绝主机值超出列表范围的请求的代码。替换后的代码HostPortValidatorCustomizer.java共 85 行,仅用于验证端口。
前提是它SecureRequestCustomizer涵盖了通过 TLS SNI 进行的主机验证。这个前提对于 TLS 层的 Host 头部来说是正确的。但它并不适用于接下来读取的两个应用层头部(host 和X-Forwarded-Hosthost X-ProxyHost)StandardRequestUriProvider.getHost()。重构移动了信任边界,但没有更新另一侧的读取器。
受影响的版本
Apache NiFi 0.0.1 至 2.9.0 版本(根据官方公告,存在漏洞)
Apache NiFi 2.10.0(已修复)
引入此回归的 NIFI-14209 提交于 2025 年 2 月发布。2.2.0 之前的版本保留了原有的HostHeaderHandler防御机制;此回归是从该版本开始的。
修复
NiFi 2.10.0(Jira NIFI-15953,PR #11268)重新添加了允许列表验证,这次除了 Host 标头之外X-ProxyHost,还覆盖了其他标头。此修复与之前针对 context-path 标头的处理方式一致。公告文本如下:X-Forwarded-HostgetPath()
Apache NiFi 2.10.0 基于 nifi.web.proxy.host 属性实现了对 X-ProxyHost 和 X-Forwarded-Host HTTP 请求头的验证。启用请求头验证需要将应用程序配置为使用 HTTPS。位于 Apache NiFi 前端的反向代理服务器负责过滤输入请求头,并将允许的值提供给应用程序。
对于使用 2.2.0 到 2.9.0 版本且无法立即升级的运维人员,缓解措施是在反向代理上剥离这些标头X-Forwarded-Host,X-ProxyHost然后再将其传递到 NiFi。默认情况下nifi.properties不会对此发出警告;如果您在任何转发来自不受信任客户端的这些标头的服务器后运行 NiFi,请务必进行审计。
披露
已于 2026 年 5 月 15 日提交报告,[email protected]并附有上述实时复现步骤。Apache 方面迅速完成了漏洞分类和补丁开发。已分配 CVE 编号,PR 已合并,安全公告已于 2026 年 6 月 20 日发布。
链接
安全公告:https://nifi.apache.org/documentation/security/#CVE-2026-54665
CVE 记录:https://www.cve.org/CVERecord?id= CVE-2026-54665
NVD:https://nvd.nist.gov/vuln/detail/CVE-2026-54665
jira:https://issues.apache.org/jira/browse/NIFI-15953
修复 PR:https://github.com/apache/nifi/pull/11268
CWE-444:https://cwe.mitre.org/data/definitions/444.html
CWE-601:https://cwe.mitre.org/data/definitions/601.html
END
公众号内容都来自国外平台-所有文章可通过点击阅读原文到达原文地址或参考地址
排版 编辑 | Ots 小安
采集 翻译 | Ots Ai牛马
公众号 | AnQuan7 (Ots安全)
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Ots安全 《CVE-2026-54665:Apache NiFi 中的预认证主机头注入漏洞》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论