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
|