Issue
Posted in JBoss forums as well: https://developer.jboss.org/thread/280195
update 2019-06-26 apparently this is now confirmed as a bug in Undertow, with a pull request submitted here.
This is a SSCCE.
I have a very simple Servlet that does nothing except print the value of a parameter:
public class TestServlet extends HttpServlet{
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
final String URL = req.getParameter("url");
System.out.printf("url parameter read as: [%s]\n", URL);
}
}
My application's web.xml is configured so as to automatically redirect http
access to https
:
<web-app>
...
<security-constraint>
<web-resource-collection>
<web-resource-name>SECURE</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
</web-app>
… and I also have (in my standalone-full.xml
configuration file) the redirect-socket
attribute set in the definition of the http-listener
:
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
If I deploy to JBoss EAP 7.1 and enter the following URL in my browser (where the url
parameter carries the URL-encoded value of "http://www.google.com"):
http://localhost:8082/get-parameter-test/min?url=http%3A%2F%2Fwww.google.com
… this is what I see in the developer console:
As a result, after the automatic redirect, my code fails to obtain the correct value of the url
parameter and I see in the log files:
url parameter read as: [http%3A%2F%2Fwww.google.com]
However, if I deploy to JBoss EAP 6.2 and do the same, the URL is not mangled in the redirect and everything works fine:
update
this answer suggests that the decode-url
parameter in the configuration of the http-listener
and https-listener
in the undertow system in the JBoss configuration file (standalong-full.xml
) may have something to do with this. This is wrong. I tried all four combinations:
- http-listener:
decode-url
="false" and https-listener:decode-url
="false" - http-listener:
decode-url
="false" and https-listener:decode-url
="true" - http-listener:
decode-url
="true" and https-listener:decode-url
="false" - http-listener:
decode-url
="true" and https-listener:decode-url
="true"
In all cases, the 302 response that's effecting the redirect from http to https has the following header:
Location: https://localhost:8445/get-parameter-test?url=http%253A%252F%252Fwww.google.com
That is in all cases, the URL is mangled (call it re-encoded it if you like, it's mangled AFAIAC). There's no reason for this behavior at all and it is not what EAP 6.2 does. The value of the decode-url
parameter only affects the behavior of the HttpServletRequest#getRequest
method inside your servlet, it has no effect whatsoever in the redirected URL.
Solution
update 2019-06-26 Apparently this is now confirmed as a bug in Undertow, with a pull request submitted here
Here's what ultimately worked for me.
First of all, I removed from my web.xml
the entire <security-constraint>
element as it is not needed for the solution I implemented.
I also removed the redirect-socket="https"
from the <http-listener>
configuration. That too, is also not needed. So here's what my <http-listener>
and <https-listener>
look like:
<http-listener name="default" socket-binding="http" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
I think the above is exactly what you get in JBoss EAP 7.1 out of the box, so no need to change that.
I then created a filter and added it in the <filters>
element of the undertow subsystem:
<rewrite name="http-to-https" redirect="true" target="https://%h:8445%U%q"/>
%h
is the remote host name%U
is the requested URL path%q
is the query string (automatically prepended with?
if it exists)
I found the above codes here - I am sure there's a more normative reference somewhere else but they seem to work.
Finally, I added a reference to the filter, along with a predicate, in the <server>/<host>
element (also in the undertow subsystem):
<server name="default-server">
<http-listener name="default" socket-binding="http" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<filter-ref name="server-header"/>
<filter-ref name="x-powered-by-header"/>
<filter-ref name="http-to-https" predicate="equals(%p, 8082)"/>
<http-invoker security-realm="ApplicationRealm"/>
</host>
</server>
With the above configuration the request gets redirected without re-encoding the URL:
$ curl -I -L -k http://localhost:8082/get-parameter-test?url=http%3A%2F%2Fwww.google.com
HTTP/1.1 302 Found
Connection: keep-alive
Server: JBoss-EAP/7
Location: https://127.0.0.1:8445/get-parameter-test?url=http%3A%2F%2Fwww.google.com
Content-Length: 0
Date: Tue, 11 Jun 2019 17:43:23 GMT
HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: JBoss-EAP/7
Content-Length: 0
Date: Tue, 11 Jun 2019 17:43:23 GMT
… and the parameter is correctly read from Java:
url parameter read as: [http://www.google.com]
There is no need to set decode-url="true"
in the http/https listeners as that's the default value.
NB: The above causes JBoss EAP 7.1 to send a 302 redirect. I have no idea how to configure a 303 or 307 redirect.
Final remark
The obvious alternative to the above is to do the redirect programmatically from your application's code using HttpServletRequest#sendRedirect. In that case too, you do not need redirect-socket="https"
in your http-listener
.
Apparently, the redirect-socket
attribute is only necessary in conjunction with the <security-constraint>
element in your application's web.xml. That's because otherwise (i.e. if you have the <security-constraint>
in your web.xml
but no redirect-socket
in your http-listener
), you get hit with:
ERROR [io.undertow.request] (default task-14) UT005001: An exception occurred processing the request: java.lang.IllegalStateException: UT010053: No confidential port is available to redirect the current request.
).
However if you have both the <security-constraint>
and the redirect-socket
, the query String is needlessly re-URL-encoded (and thus mangled) in the redirected URL as explained in the question. So it's not clear to me what's the use of <security-constraint>
in JBoss EAP 7.1.
Answered By - Marcus Junius Brutus