Warm tip: This article is reproduced from stackoverflow.com, please click
java spring struts2 tomcat tomcat7

Struts relative path flattened in 7.0.78 but not in 7.0.79, breaking isNormalized

发布于 2020-04-18 10:03:36

We're returning a @Result from a Struts 2 action in a Spring Boot app that specifies a location containing a relative path, in order to reference a jsp in a sibling directory to the root of the rest of the application.

@Result(name = "foo", location = "../../cat/bar.jsp")

This works in Tomcat 7.0.78, arriving in the StrictHttpFirewall.getFirewalledRequest as:

ApplicationHttpRequest.requestURI = "rootParent/cat/bar.jsp"

In Tomcat 7.0.79+, this flattening is no longer happening, and when the request reaches StrictHttpFirewall to check for url normalization, it blows up because it arrives as:

ApplicationHttpRequest.requestURI = "rootParent/root/WEB-INF/../../cat/bar.jsp"

I've scoured the Apache 7 changelog and security fixes to see if there is anything that might cause this to occur, but can find nothing. I've tried tweaking the useRelativeRedirects Context property, but it doesn't seem to have any effect. Pulling my hair out stepping through filter chains in debug. Any help would be much appreciated!

Questioner
Alex Pritchard
Viewed
45
Alex Pritchard 2020-02-05 01:05

I figured out the problem and found a solution.

The problem is that this commit changes the behavior of org.apache.catalina.core.ApplicationContext.getRequestDispatcher between 7.0.78 and 7.0.79. Previously, the provided URL was normalized before being appended to the context path. In 7.0.79 and beyond, the normalized version is no longer being used.

There are no settings that change this behavior, but I figured out how to resolve the issue by modifying the location Struts passes in. By the time the request reaches ApplicationContext, struts already has combined the context and url into a single location, but we can override the context portion in the Request annotation. It uses "WEB-INF/content/" + "%{url}" for everything else, so I changed it to just "%{url}".

Before, results in "WEB-INF/content/../../bar.jsp"

@Result(name = "foo", location = "../../bar.jsp")

After, results in "bar.jsp"

@Result(name = "foo", location = "bar.jsp", params = {"location", "%{url}"})