CVE-2018-17193
Description
The message-page.jsp error page used the value of the HTTP request header X-ProxyContextPath without sanitization, resulting in a reflected XSS attack. Mitigation: The fix to correctly parse and sanitize the request attribute value was applied on the Apache NiFi 1.8.0 release. Users running a prior 1.x release should upgrade to the appropriate release.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache NiFi 1.x before 1.8.0 is vulnerable to reflected XSS via the X-ProxyContextPath HTTP header in the error page.
Vulnerability
The message-page.jsp error page in Apache NiFi 1.x prior to 1.8.0 directly includes the value of the HTTP request header X-ProxyContextPath without sanitization, leading to a reflected cross-site scripting (XSS) vulnerability [1][2][3]. This affects all NiFi 1.x releases before 1.8.0.
Exploitation
An attacker can craft a malicious URL or HTTP request containing a X-ProxyContextPath header with embedded JavaScript. When a victim visits a page that triggers an error (e.g., a non-existent resource), the NiFi server returns the error page with the unsanitized header value, causing the script to execute in the victim's browser. No authentication is required; the attacker only needs to lure the victim to a crafted link.
Impact
Successful exploitation allows the attacker to execute arbitrary JavaScript in the context of the victim's session on the NiFi web interface. This can lead to session hijacking, defacement, or theft of sensitive information displayed on the page.
Mitigation
The fix was applied in Apache NiFi 1.8.0, which correctly parses and sanitizes the request attribute value [1][2]. Users running any prior 1.x release should upgrade to NiFi 1.8.0 or later. No workaround is documented; upgrading is the recommended action.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.nifi:nifiMaven | >= 1.0.0, < 1.8.0 | 1.8.0 |
Affected products
2- Apache Software Foundation/Apache NiFiv5Range: Apache NiFi 1.0.0 - 1.7.1
Patches
1e62aa0252dfcNIFI-5442 Get X-ProxyContextPath value from request attributes rather than directly from headers.
9 files changed · +192 −89
nifi-commons/nifi-web-utils/src/main/java/org/apache/nifi/web/filter/SanitizeContextPathFilter.java+81 −0 added@@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import org.apache.nifi.web.util.WebUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This filter intercepts a request and populates the {@code contextPath} attribute on the request with a sanitized value (originally) retrieved from {@code nifi.properties}. + */ +public class SanitizeContextPathFilter implements Filter { + private static final Logger logger = LoggerFactory.getLogger(SanitizeContextPathFilter.class); + + private String whitelistedContextPaths = ""; + + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + String providedWhitelist = filterConfig.getServletContext().getInitParameter("whitelistedContextPaths"); + logger.debug("SanitizeContextPathFilter received provided whitelisted context paths from NiFi properties: " + providedWhitelist); + if (providedWhitelist != null) { + whitelistedContextPaths = providedWhitelist; + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { + // Inject the contextPath attribute into the request + injectContextPathAttribute(request); + + // Pass execution to the next filter in the chain + filterChain.doFilter(request, response); + } + + /** + * Determines, sanitizes, and injects the {@code contextPath} attribute into the {@code request}. If not present, an empty string {@code ""} is injected. + * @param request the request + */ + protected void injectContextPathAttribute(ServletRequest request) { + // Capture the provided context path headers and sanitize them before using in the response + String contextPath = WebUtils.sanitizeContextPath(request, whitelistedContextPaths, ""); + request.setAttribute("contextPath", contextPath); + + logger.debug("SanitizeContextPathFilter set contextPath: " + contextPath); + } + + @Override + public void destroy() { + } + + /** + * Getter for whitelistedContextPaths. Cannot be package-private because of an issue where the package is scoped per classloader. + * + * @return the whitelisted context path(s) + */ + protected String getWhitelistedContextPaths() { + return whitelistedContextPaths; + } +}
nifi-commons/nifi-web-utils/src/main/java/org/apache/nifi/web/util/WebUtils.java+23 −2 modified@@ -21,13 +21,12 @@ import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; - import javax.net.ssl.SSLContext; +import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.core.UriBuilderException; - import org.apache.commons.lang3.StringUtils; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider; @@ -176,6 +175,28 @@ public static String normalizeContextPath(String determinedContextPath) { } } + /** + * Returns a "safe" context path value from the request headers to use in a proxy environment. + * This is used on the JSP to build the resource paths for the external resources (CSS, JS, etc.). + * If no headers are present specifying this value, it is an empty string. + * + * @param request the HTTP request + * @return the context path safe to be printed to the page + */ + public static String sanitizeContextPath(ServletRequest request, String whitelistedContextPaths, String jspDisplayName) { + if (StringUtils.isBlank(jspDisplayName)) { + jspDisplayName = "JSP page"; + } + String contextPath = normalizeContextPath(determineContextPath((HttpServletRequest) request)); + try { + verifyContextPath(whitelistedContextPaths, contextPath); + return contextPath; + } catch (UriBuilderException e) { + logger.error("Error determining context path on " + jspDisplayName + ": " + e.getMessage()); + return ""; + } + } + /** * Determines the context path if populated in {@code X-ProxyContextPath} or {@code X-ForwardContext} headers. If not populated, returns an empty string. *
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java+35 −35 modified@@ -18,6 +18,40 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.NiFiServer; @@ -68,41 +102,6 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; -import javax.servlet.DispatcherType; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.stream.Collectors; - /** * Encapsulates the Jetty instance. */ @@ -317,6 +316,7 @@ private Handler loadWars(final Set<Bundle> bundles) { final WebAppContext webUiContext = loadWar(webUiWar, "/nifi", frameworkClassLoader); webUiContext.getInitParams().put("oidc-supported", String.valueOf(props.isOidcEnabled())); webUiContext.getInitParams().put("knox-supported", String.valueOf(props.isKnoxSsoEnabled())); + webUiContext.getInitParams().put("whitelistedContextPaths", props.getWhitelistedContextPaths()); handlers.addHandler(webUiContext); // load the web api app
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java+4 −4 modified@@ -340,8 +340,8 @@ public void oidcLogout(@Context HttpServletRequest httpServletRequest, @Context httpServletResponse.sendRedirect(postLogoutRedirectUri); } else { URI logoutUri = UriBuilder.fromUri(endSessionEndpoint) - .queryParam("post_logout_redirect_uri", postLogoutRedirectUri) - .build(); + .queryParam("post_logout_redirect_uri", postLogoutRedirectUri) + .build(); httpServletResponse.sendRedirect(logoutUri.toString()); } } @@ -765,7 +765,7 @@ private long validateTokenExpiration(long proposedTokenExpiration, String identi * Gets the value of a cookie matching the specified name. If no cookie with that name exists, null is returned. * * @param cookies the cookies - * @param name the name of the cookie + * @param name the name of the cookie * @return the value of the corresponding cookie, or null if the cookie does not exist */ private String getCookieValue(final Cookie[] cookies, final String name) { @@ -786,7 +786,7 @@ private String getOidcCallback() { private String getNiFiUri() { final String nifiApiUrl = generateResourceUri(); - final String baseUrl = StringUtils.substringBeforeLast(nifiApiUrl,"/nifi-api"); + final String baseUrl = StringUtils.substringBeforeLast(nifiApiUrl, "/nifi-api"); return baseUrl + "/nifi"; }
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-error/src/main/java/org/apache/nifi/web/filter/CatchAllFilter.java+16 −36 modified@@ -17,65 +17,45 @@ package org.apache.nifi.web.filter; import java.io.IOException; -import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.UriBuilderException; -import org.apache.nifi.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Filter for forward all requests to index.jsp. + * This filter catches all requests and explicitly forwards them to a JSP ({@code index.jsp} injected in + * the filter configuration. This is used to handle all errors (this module is only used for errors). It + * extends {@link SanitizeContextPathFilter} which sanitizes the context path and injects it as a request + * attribute to be used on the page for linking resources without XSS vulnerabilities. */ -public class CatchAllFilter implements Filter { +public class CatchAllFilter extends SanitizeContextPathFilter { private static final Logger logger = LoggerFactory.getLogger(CatchAllFilter.class); - private static String whitelistedContextPaths = ""; + private String forwardPath = ""; + private String displayPath = ""; @Override public void init(FilterConfig filterConfig) throws ServletException { - String providedWhitelist = filterConfig.getServletContext().getInitParameter("whitelistedContextPaths"); - logger.debug("CatchAllFilter received provided whitelisted context paths from NiFi properties: " + providedWhitelist); - if (providedWhitelist != null) { - whitelistedContextPaths = providedWhitelist; - } + // TODO: Perform path validation (against what set of rules)? + forwardPath = filterConfig.getInitParameter("forwardPath"); + displayPath = filterConfig.getInitParameter("displayPath"); + + logger.debug("CatchAllFilter [" + displayPath + "] received provided whitelisted context paths from NiFi properties: " + getWhitelistedContextPaths()); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { - // Capture the provided context path headers and sanitize them before using in the response - String contextPath = getSanitizedContextPath(request); - request.setAttribute("contextPath", contextPath); + // Inject the contextPath attribute into the request + injectContextPathAttribute(request); - // for all requests to index.jsp - request.getRequestDispatcher("/index.jsp").forward(request, response); + // Forward all requests to index.jsp + request.getRequestDispatcher(forwardPath).forward(request, response); } @Override public void destroy() { } - - /** - * Returns a "safe" context path value from the request headers to use in a proxy environment. - * This is used on the JSP to build the resource paths for the external resources (CSS, JS, etc.). - * If no headers are present specifying this value, it is an empty string. - * - * @param request the HTTP request - * @return the context path safe to be printed to the page - */ - private String getSanitizedContextPath(ServletRequest request) { - String contextPath = WebUtils.normalizeContextPath(WebUtils.determineContextPath((HttpServletRequest) request)); - try { - WebUtils.verifyContextPath(whitelistedContextPaths, contextPath); - return contextPath; - } catch (UriBuilderException e) { - logger.error("Error determining context path on index.jsp: " + e.getMessage()); - return ""; - } - } }
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-error/src/main/webapp/WEB-INF/web.xml+8 −0 modified@@ -18,6 +18,14 @@ <filter> <filter-name>catch-all-filter</filter-name> <filter-class>org.apache.nifi.web.filter.CatchAllFilter</filter-class> + <init-param> + <param-name>displayPath</param-name> + <param-value>index.jsp</param-value> + </init-param> + <init-param> + <param-name>forwardPath</param-name> + <param-value>/index.jsp</param-value> + </init-param> </filter> <filter-mapping> <filter-name>catch-all-filter</filter-name>
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml+5 −0 modified@@ -923,6 +923,11 @@ <artifactId>nifi-utils</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-web-utils</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId>
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp+5 −12 modified@@ -17,18 +17,11 @@ <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> - <% - String contextPath = request.getHeader("X-ProxyContextPath"); - if (contextPath == null) { - contextPath = request.getHeader("X-Forwarded-Context"); - } - if (contextPath == null) { - contextPath = ""; - } - if (contextPath.endsWith("/")) { - contextPath = contextPath.substring(0, contextPath.length() - 1); - } - %> +<% + // Sanitize the contextPath to ensure it is on this server + // rather than getting it from the header directly + String contextPath = request.getAttribute("contextPath").toString(); +%> <head> <title><%= request.getAttribute("title") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("title").toString()) %></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml+15 −0 modified@@ -146,6 +146,21 @@ <url-pattern>/logout</url-pattern> </filter-mapping> + <!-- catch all filter --> + <filter> + <filter-name>SanitizeContextPathFilter</filter-name> + <filter-class>org.apache.nifi.web.filter.SanitizeContextPathFilter</filter-class> + </filter> + <filter-mapping> + <filter-name>SanitizeContextPathFilter</filter-name> + <url-pattern>/*</url-pattern> + <dispatcher>REQUEST</dispatcher> + <dispatcher>FORWARD</dispatcher> + <dispatcher>INCLUDE</dispatcher> + <dispatcher>ERROR</dispatcher> + <dispatcher>ASYNC</dispatcher> + </filter-mapping> + <welcome-file-list> <welcome-file>canvas.jsp</welcome-file> <welcome-file>/WEB-INF/pages/canvas.jsp</welcome-file>
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-4qq9-rrq6-48ffghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-17193ghsaADVISORY
- github.com/apache/nifi/commit/e62aa0252dfcf34dff0c3a9c51265b1d0f9dfc9fghsaWEB
- issues.apache.org/jira/browse/NIFI-5442ghsaWEB
- nifi.apache.org/security.htmlghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.