Undertow (JBoss 7) mangles / re-encodes URL-encoded 重定向参数
Undertow (JBoss 7) mangles / re-encodes URL-encoded parameter on redirect
也在 JBoss 论坛中发布:
https://developer.jboss.org/thread/280195
更新 2019-06-26 显然这现在被确认为 Undertow 中的一个错误,提交了拉取请求 here。
这是一个 SSCCE。
我有一个非常简单的 Servlet,它除了打印参数值外什么都不做:
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);
}
}
我的应用程序的 web.xml 配置为自动重定向 http
访问 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>
… 我也有(在我的 standalone-full.xml
配置文件中)redirect-socket
属性集在 http-listener
:
的定义中
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
如果我部署到 JBoss EAP 7.1 并在我的浏览器中输入以下 URL(其中 url
参数携带 URL-encoded 值“http://www.google.com"):
http://localhost:8082/get-parameter-test/min?url=http%3A%2F%2Fwww.google.com
…这是我在开发者控制台看到的:
因此,在自动重定向之后,我的代码无法获取 url
参数的正确值,我在日志文件中看到:
url parameter read as: [http%3A%2F%2Fwww.google.com]
但是,如果我部署到 JBoss EAP 6.2 并执行相同的操作,URL 不会在重定向中被破坏并且一切正常:
更新
提示JBoss配置文件中undertow系统中http-listener
和https-listener
配置中的decode-url
参数(standalong-full.xml
) 可能与此有关。这是错误的。我尝试了所有四种组合:
- http-listener:
decode-url
="false" 和 https-listener:decode-url
="false"
- http-listener:
decode-url
="false" 和 https-listener:decode-url
="true"
- http-listener:
decode-url
="true" 和 https-listener:decode-url
="false"
- http-listener:
decode-url
="true" 和 https-listener:decode-url
="true"
在所有情况下,影响从 http 到 https 重定向的 302 响应具有以下 header:
Location: https://localhost:8445/get-parameter-test?url=http%253A%252F%252Fwww.google.com
也就是说,在所有情况下,URL 都被破坏了(如果你愿意,可以称它为 re-encoded,它被破坏了 AFAIAC)。这种行为根本没有理由,这不是 EAP 6.2 所做的。 decode-url
参数的值只影响 servlet 中 HttpServletRequest#getRequest
方法的行为,它对重定向的 URL.
没有任何影响
你 URL 没有被破坏它正在 re-encoded。从 http 重定向到 https 时,问题是 re-encoding 或 URL。您正在将编码的 url 参数传递给 http
即 http%3A%2F%2Fwww.google.com.
http://www.google.com --encode--> http%3A%2F%2Fwww.google.com --re-encode--> http%253A%252F%252Fwww.google.com
在您 re-encode url 中,“%”被编码为“%25”。
要禁用此行为,您需要对监听器进行更改。有一个名为 decode-url 的属性可用于 disable/enable 这一行为。
decode-url
: If the URL should be decoded. If this is not set to
true then percent encoded characters in the URL will be left as is.
有关 Undertow 子系统的更多信息,请参阅以下 link。
https://docs.jboss.org/author/display/WFLY/Undertow+subsystem+configuration
注意:此功能可能在 JBoss 6.2 上有效,因为在 JBoss 6.2 中,此配置的子系统称为 WEB,在中被 Undertow 取代JBoss EAP 7+
update 2019-06-26 Apparently this is now confirmed as a bug in
Undertow, with a pull request submitted here
以下是最终对我有用的方法。
首先,我从 web.xml
中删除了整个 <security-constraint>
元素,因为我实施的解决方案不需要它。
我还从 <http-listener>
配置中删除了 redirect-socket="https"
。那也一样,也不需要。所以这就是我的 <http-listener>
和 <https-listener>
的样子:
<http-listener name="default" socket-binding="http" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
我认为以上内容正是您在 JBoss EAP 7.1 中开箱即用的内容,因此无需更改。
然后我创建了一个过滤器并将其添加到 undertow 子系统的 <filters>
元素中:
<rewrite name="http-to-https" redirect="true" target="https://%h:8445%U%q"/>
%h
是远程主机名
%U
是请求的URL路径
%q
是查询字符串(如果存在则自动添加 ?
前缀)
我找到了上面的代码 here - 我确信在其他地方有更规范的参考,但它们似乎有效。
最后,我在 <server>/<host>
元素(也在 undertow 子系统中)添加了对过滤器的引用以及谓词:
<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>
使用上述配置,请求在没有 re-encoding 的情况下被重定向 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
… 并且参数从 Java:
中正确读取
url parameter read as: [http://www.google.com]
无需在 http/https 侦听器中设置 decode-url="true"
,因为这是默认值。
注意:以上导致 JBoss EAP 7.1 发送 302 重定向。我不知道如何配置 303 或 307 重定向。
最后的评论
上述方法的明显替代方法是使用 HttpServletRequest#sendRedirect 从您的应用程序代码中以编程方式进行重定向。在那种情况下,您 不需要 在 http-listener
.
中需要 redirect-socket="https"
显然,redirect-socket
属性只需要与应用程序的 web.xml 中的 <security-constraint>
元素结合使用。那是因为否则(即如果您的 web.xml
中有 <security-constraint>
但 http-listener
中没有 redirect-socket
),您会遇到:
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.
).
但是,如果您同时拥有 <security-constraint>
和 redirect-socket
,查询字符串在重定向的 URL 中是不必要的 re-URL-encoded(因此被破坏),如中所述这个问题。所以我不清楚 <security-constraint>
在 JBoss EAP 7.1.
中有什么用
也在 JBoss 论坛中发布: https://developer.jboss.org/thread/280195
更新 2019-06-26 显然这现在被确认为 Undertow 中的一个错误,提交了拉取请求 here。
这是一个 SSCCE。
我有一个非常简单的 Servlet,它除了打印参数值外什么都不做:
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);
}
}
我的应用程序的 web.xml 配置为自动重定向 http
访问 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>
… 我也有(在我的 standalone-full.xml
配置文件中)redirect-socket
属性集在 http-listener
:
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
如果我部署到 JBoss EAP 7.1 并在我的浏览器中输入以下 URL(其中 url
参数携带 URL-encoded 值“http://www.google.com"):
http://localhost:8082/get-parameter-test/min?url=http%3A%2F%2Fwww.google.com
…这是我在开发者控制台看到的:
因此,在自动重定向之后,我的代码无法获取 url
参数的正确值,我在日志文件中看到:
url parameter read as: [http%3A%2F%2Fwww.google.com]
但是,如果我部署到 JBoss EAP 6.2 并执行相同的操作,URL 不会在重定向中被破坏并且一切正常:
更新
http-listener
和https-listener
配置中的decode-url
参数(standalong-full.xml
) 可能与此有关。这是错误的。我尝试了所有四种组合:
- http-listener:
decode-url
="false" 和 https-listener:decode-url
="false" - http-listener:
decode-url
="false" 和 https-listener:decode-url
="true" - http-listener:
decode-url
="true" 和 https-listener:decode-url
="false" - http-listener:
decode-url
="true" 和 https-listener:decode-url
="true"
在所有情况下,影响从 http 到 https 重定向的 302 响应具有以下 header:
Location: https://localhost:8445/get-parameter-test?url=http%253A%252F%252Fwww.google.com
也就是说,在所有情况下,URL 都被破坏了(如果你愿意,可以称它为 re-encoded,它被破坏了 AFAIAC)。这种行为根本没有理由,这不是 EAP 6.2 所做的。 decode-url
参数的值只影响 servlet 中 HttpServletRequest#getRequest
方法的行为,它对重定向的 URL.
你 URL 没有被破坏它正在 re-encoded。从 http 重定向到 https 时,问题是 re-encoding 或 URL。您正在将编码的 url 参数传递给 http
即 http%3A%2F%2Fwww.google.com.
http://www.google.com --encode--> http%3A%2F%2Fwww.google.com --re-encode--> http%253A%252F%252Fwww.google.com
在您 re-encode url 中,“%”被编码为“%25”。
要禁用此行为,您需要对监听器进行更改。有一个名为 decode-url 的属性可用于 disable/enable 这一行为。
decode-url
: If the URL should be decoded. If this is not set to true then percent encoded characters in the URL will be left as is.
有关 Undertow 子系统的更多信息,请参阅以下 link。 https://docs.jboss.org/author/display/WFLY/Undertow+subsystem+configuration
注意:此功能可能在 JBoss 6.2 上有效,因为在 JBoss 6.2 中,此配置的子系统称为 WEB,在中被 Undertow 取代JBoss EAP 7+
update 2019-06-26 Apparently this is now confirmed as a bug in Undertow, with a pull request submitted here
以下是最终对我有用的方法。
首先,我从 web.xml
中删除了整个 <security-constraint>
元素,因为我实施的解决方案不需要它。
我还从 <http-listener>
配置中删除了 redirect-socket="https"
。那也一样,也不需要。所以这就是我的 <http-listener>
和 <https-listener>
的样子:
<http-listener name="default" socket-binding="http" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
我认为以上内容正是您在 JBoss EAP 7.1 中开箱即用的内容,因此无需更改。
然后我创建了一个过滤器并将其添加到 undertow 子系统的 <filters>
元素中:
<rewrite name="http-to-https" redirect="true" target="https://%h:8445%U%q"/>
%h
是远程主机名%U
是请求的URL路径%q
是查询字符串(如果存在则自动添加?
前缀)
我找到了上面的代码 here - 我确信在其他地方有更规范的参考,但它们似乎有效。
最后,我在 <server>/<host>
元素(也在 undertow 子系统中)添加了对过滤器的引用以及谓词:
<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>
使用上述配置,请求在没有 re-encoding 的情况下被重定向 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
… 并且参数从 Java:
中正确读取url parameter read as: [http://www.google.com]
无需在 http/https 侦听器中设置 decode-url="true"
,因为这是默认值。
注意:以上导致 JBoss EAP 7.1 发送 302 重定向。我不知道如何配置 303 或 307 重定向。
最后的评论
上述方法的明显替代方法是使用 HttpServletRequest#sendRedirect 从您的应用程序代码中以编程方式进行重定向。在那种情况下,您 不需要 在 http-listener
.
redirect-socket="https"
显然,redirect-socket
属性只需要与应用程序的 web.xml 中的 <security-constraint>
元素结合使用。那是因为否则(即如果您的 web.xml
中有 <security-constraint>
但 http-listener
中没有 redirect-socket
),您会遇到:
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.
).
但是,如果您同时拥有 <security-constraint>
和 redirect-socket
,查询字符串在重定向的 URL 中是不必要的 re-URL-encoded(因此被破坏),如中所述这个问题。所以我不清楚 <security-constraint>
在 JBoss EAP 7.1.