This issue has been created
There is 1 update.
 
 
XWiki Platform / cid:jira-generated-image-avatar-0ea78abc-c590-478c-ab6f-a0fba3ffabd3 XWIKI-24711 Open

Support a trusted proxy count when reading the X-Forwarded-* and Forwarded headers

 
View issue   ยท   Add comment
 

Issue created

 
cid:jira-generated-image-avatar-ede2011c-ba7d-4b08-8c05-4825ce441606 Eleni created this issue on 18/Aug/26 14:04
 
Summary: Support a trusted proxy count when reading the X-Forwarded-* and Forwarded headers
Issue Type: cid:jira-generated-image-avatar-0ea78abc-c590-478c-ab6f-a0fba3ffabd3 Improvement
Affects Versions: 10.7
Assignee: Unassigned
Components: Containers
Created: 18/Aug/26 14:04
Priority: cid:jira-generated-image-static-major-483cc212-e0d7-48f6-a106-d15c3c3e693d Major
Reporter: Eleni
Description:

What happens

HttpServletUtils builds the URL XWiki considers its own from the Forwarded, X-Forwarded-Host and X-Forwarded-Proto headers, and always takes the first value of each:

  • getFirstHeaderValue() returns the substring before the first comma, and is what appendScheme() and appendHostPort() use.
  • ForwardedHeader does the same for the RFC 7239 Forwarded header.
  • getClientIP() does the same for X-Forwarded-For, and already carries a TODO describing the fix proposed below.

A proxy appends its own value to whatever the client sent, so the first value is the client's, not the proxy's. XWiki therefore reads a value the client controls, unless the proxy is configured to drop the incoming headers first.

Why it matters

XWiki builds the links it emits from the host and scheme it reads there. On a proxy that only adds its own values, a request carrying X-Forwarded-Host: attacker.example.com makes the wiki emit its links for that host. There is no configuration for the number of proxies in front of the wiki, so XWiki cannot tell which value is the trustworthy one, and the documentation can only tell administrators to overwrite those headers at the proxy. The XWiki documentation now does exactly that on both proxy subtrees: the Apache pages carry RequestHeader unset for the three headers, and the NginX ones set X-Forwarded-Host and empty Forwarded.

That instruction is the right one either way, but it makes correct behaviour depend on every administrator getting a proxy configuration right, including on proxies XWiki is put behind by someone who never reads those pages.

Suggested improvement

Make the number of trusted proxies configurable (0 by default) and select the value by counting from the right, as selecting an IP address describes and as the TODO in getClientIP() already proposes: with no proxy declared, ignore the headers entirely; with one, take the last value; with two, the one before last. Applying it to all four headers at once keeps getClientIP() and the URL building consistent.

Where

 
 

1 update

 
cid:jira-generated-image-avatar-ede2011c-ba7d-4b08-8c05-4825ce441606 Changes by Eleni on 18/Aug/26 14:06
 
Description: h2. What happens

{{HttpServletUtils}} builds the URL XWiki considers its own from the {{{}Forwarded{}}}, {{X-Forwarded-Host}} and {{X-Forwarded-Proto}} headers, and always takes the *first* value of each:
* {{getFirstHeaderValue()}} returns the substring before the first comma, and is what {{appendScheme()}} and {{appendHostPort()}} use.
* {{ForwardedHeader}} does the same for the RFC 7239 {{Forwarded}} header.
* {{getClientIP()}} does the same for {{{}X-Forwarded-For{}}}, and already carries a TODO describing the fix proposed below.

A proxy *appends* its own value to whatever the client sent, so the first value is the client's, not the proxy's. XWiki therefore reads a value the client controls, unless the proxy is configured to drop the incoming headers first.
h2. Why it matters

XWiki builds the links it emits from the host and scheme it reads there. On a proxy that only adds its own values, a request carrying {{X-Forwarded-Host: attacker.example.com}} makes the wiki emit its links for that host. There is no configuration for the number of proxies in front of the wiki, so XWiki cannot tell which value is the trustworthy one, and the documentation can only tell administrators to overwrite those headers at the proxy. The XWiki documentation now does exactly that on both proxy subtrees: the Apache pages carry {{RequestHeader unset}} for the three headers, and the NginX ones set {{X-Forwarded-Host}} and empty {{{}Forwarded{}}}.

That instruction is the right one either way, but it makes correct behaviour depend on every administrator getting a proxy configuration right, including on proxies XWiki is put behind by someone who never reads those pages.
h2. Suggested improvement

Make the number of trusted proxies configurable (0 by default) and select the value by counting from the {*}right{*}, as [selecting an IP address|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#selecting_an_ip_address] describes and as the TODO in {{getClientIP()}} already proposes: with no proxy declared, ignore the headers entirely; with one, take the last value; with two, the one before last. Applying it to all four headers at once keeps {{getClientIP()}} and the URL building consistent.
h2. Where
* [{{xwiki-platform-core/xwiki-platform-container/xwiki-platform-container-servlet/src/main/java/org/xwiki/container/servlet/HttpServletUtils.java}}|https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-container/xwiki-platform-container-servlet/src/main/java/org/xwiki/container/servlet/HttpServletUtils.java]
** {{{}getFirstHeaderValue{}}},
** {{{}appendScheme{}}},
** {{{}appendHostPort{}}},
** {{getClientIP}}
* [{{xwiki-platform-core/xwiki-platform-container/xwiki-platform-container-servlet/src/main/java/org/xwiki/container/servlet/internal/ForwardedHeader.java}}|https://github.com/xwiki/xwiki-platform/blob/master/xwiki-platform-core/xwiki-platform-container/xwiki-platform-container-servlet/src/main/java/org/xwiki/container/servlet/internal/ForwardedHeader.java]


The header support itself was added by XWIKI-15505 and XWIKI-15506, both released in 10.7.