55

This is not a duplicate referenced question, because it is Spring specific. Whoever added that (3 years after the fact!) didn't bother to read the question or comment thread to see what the real answer was. The accepted answer isn't quite the answer, but the author of the answer never came back and edited it like I asked.

Given the restful method below, Spring 3.1 gives a 400 error with "The request sent by the client was syntactically incorrect ()." when the token parameter contains a URL encoded slash (%2F), for example "https://somewhere.com/ws/stuff/lookup/resourceId/287559/token/R4o6lI%2FbBx43/userName/jim" Without the %2F everything works fine. A 3rd party is already calling this service (of course!) so I can't change what they send, in the short term at least. Any ideas on how to work around this on the server side?

This problem is described very well here https://jira.springsource.org/browse/SPR-8662 though that issue is related to UriTemplate which I am not using that I can tell.

@RequestMapping("/ws/stuff/**")
@Controller
public class StuffController {
  @RequestMapping(value = "/ws/stuff/lookup/resourceId/{resourceId}/token/{token}/userName/{userName}", method = RequestMethod.GET)
   public @ResponseBody
   String provisionResource(@PathVariable("resourceId") String resourceId, @PathVariable("token") String token, @PathVariable("userName") String userName, ModelMap modelMap,
         HttpServletRequest request, HttpServletResponse response) {
      return handle(resourceId, userName, request, token, modelMap);
   }
}

Note: This is on Glassfish 3.1.2, and at first it was Grizzly/Glassfish not accepting the slash, but

-Dcom.sun.grizzly.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

fixed that.

asadmin set configs.config.server-config.network-config.protocols.protocol.http-listener-2.http.encoded-slash-enabled=true

didn't seem to help.

Community
  • 1
  • 1
Jim
  • 3,476
  • 4
  • 23
  • 33
  • Possible duplicate of [urlencoded Forward slash is breaking URL](http://stackoverflow.com/questions/3235219/urlencoded-forward-slash-is-breaking-url) – MJar Sep 07 '16 at 14:03

11 Answers11

25

for spring-boot, the following did the trick

@SpringBootApplication
public class Application extends WebMvcConfigurerAdapter {

    public static void main(String[] args) throws Exception {
        System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setUrlDecode(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }

}
iamiddy
  • 3,015
  • 3
  • 30
  • 33
  • It helped with UrlPathHelperFixed which is mentioned above. Because otherwise fails with double slashes. And it parses whole path parameter correctly and doesn't need to hack from the request. – saganas Jan 11 '17 at 16:54
  • I've tried this with ``UrlPathHelperFixed`` from https://jira.spring.io/browse/SPR-11101 but it didn't work – PAX Jul 25 '18 at 06:15
  • ...using Spring Boot ``1.5.9-RELEASE`` – PAX Jul 25 '18 at 06:23
  • 3
    You'll also need this with Spring Boot 2 : https://stackoverflow.com/a/48636757/636849 In order to avoid errors such as `RequestRejectedException: The request was rejected because the URL contained a potentially malicious String "%2F"` – Lucas Cimon Feb 08 '19 at 13:52
16

2019 Update for Spring Boot 2+ / Spring (Security) 5+ / Java 8+:

As my edit to iamiddy's answer was rejected I want to also provide the complete solution for Spring Boot 2 + as an separate answer.

The WebMvcConfigurerAdapter is deprecated with Spring5 / Java8 and can be replaced directly with the Interface WebMvcConfigurer ending up with:

@SpringBootApplication
public class Application implements WebMvcConfigurer {

    public static void main(String[] args) throws Exception {
        System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setUrlDecode(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

Plus you also need to configure Spring's (Strict)HttpFirewall to avoid the blocking of encoded slashes with the error message The request was rejected because the URL contained a potentially malicious String "%2F"

@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowUrlEncodedSlash(true);    
    return firewall;
}

Spring Boot will use the above HttpFirewall Bean when available - otherwise it might be necessary to configure the WebSecurity as mentioned here:

Ethan Shepherd
  • 569
  • 3
  • 13
Ralf
  • 569
  • 4
  • 14
  • 2 things: firstly the firewall part is optional, if you do not use the spring-security of course and second - if you use jetty, just override the configurePathMatch method. I have not found anyting about jetty only that it should work unlike in tomcat, but that was not the case and I had to fix it. – hocikto Oct 27 '20 at 14:01
  • 3
    according to [Tomcat 9 docs](https://tomcat.apache.org/tomcat-9.0-doc/config/systemprops.html) the property `UDecoder.ALLOW_ENCODED_SLASH` is going to be deprecated. So it could be `@Bean` `public WebServerFactoryCustomizer tomcatCustomizer() {` `return factory -> factory.addConnectorCustomizers(` `connector -> connector.setEncodedSolidusHandling(PASS_THROUGH.getValue()));` `}` – leonid Dec 07 '20 at 14:06
  • 7
    @leonid for me it worked with DECODE `connector -> connector.setEncodedSolidusHandling(EncodedSolidusHandling.DECODE.getValue()));` – Edu Oct 11 '21 at 14:04
  • I've applied the solution it works over postman, but started to get CORS error when testing it over browser. – Ahmet Karakaya Mar 02 '23 at 08:41
  • CORS error doesn't sound like it is related to "encoded slashes"? – Ralf Mar 02 '23 at 09:37
  • I'hev fixed it. Have you also tried to test this function via Springboot test? – Ahmet Karakaya Mar 11 '23 at 07:10
  • Honestly I can't remember - that was 3,5 years ago... – Ralf Mar 13 '23 at 13:53
15

This could be your answer: urlencoded Forward slash is breaking URL

I would suggest not putting that in the path, move it to a request param instead.

Work around:

You could change the RequestMapping to

@RequestMapping(value = "/ws/stuff/lookup/resourceId/**", method = RequestMethod.GET) 

and then parse the path variables manually from the request object.

Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
Solubris
  • 3,603
  • 2
  • 22
  • 37
  • Unfortunately I can't change the 3rd party's code any time soon. I'm not using Apache, as stated, though Grizzly does have a similar configuration, which I have already turned off, as stated. This problem is now happening at the Spring level. – Jim Nov 21 '12 at 16:58
  • 1
    In that case, you could change the RequestMapping to @RequestMapping(value = "/ws/stuff/lookup/resourceId/**", method = RequestMethod.GET) and then parse the path variables manually from the request object. – Solubris Nov 21 '12 at 18:30
  • Make that an answer, Lithium, and I'll accept it :-) That is a simple and reasonable way to get this working. I say that, so that people coming and finding this later will quickly see a good solution. – Jim Nov 21 '12 at 19:20
  • 7
    It doesn't work for me. Even with "/**" I always get 400, if %2F appears in the path. – 30thh Mar 05 '13 at 11:15
  • 1
    Simple fix, but this should really be supported. Spring shouldn't decode the path _before_ it find the relevant mapping. – Josh M. Aug 12 '19 at 18:10
  • In REST, path and query params have slightly different implications. A path param is part of the resource name, and a query param is generally a filter argument passed to a named resource. There's no technical reason resource names can't include a slash character - that's exactly what URL encoding is for. – Greg Brown Dec 03 '20 at 14:47
9

For spring boot application this worked for me..

Version 1 Add

org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

to your application.properties file

Version 2 run your spring boot application like this.

static void main(String[] args) {
    System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
    SpringApplication.run this, args
}

Version 3 or run your java application with -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

This fixed %2F encoded slash path variable for me.

manukyanv07
  • 469
  • 6
  • 4
7

Here is a fix for Spring 3.2.4 (should work for other versions as well). One must overwrite the default UrlPathHelper

    public class UrlPathHelperFixed extends UrlPathHelper {

        public UrlPathHelperFixed() {
            super.setUrlDecode(false);
        }

        @Override
        public void setUrlDecode(boolean urlDecode) {
            if (urlDecode) {
                throw new IllegalArgumentException("Handler [" + UrlPathHelperFixed.class.getName() + "] does not support URL decoding.");
            }
        }

        @Override
        public String getServletPath(HttpServletRequest request) {
            return getOriginatingServletPath(request);
        }

        @Override
        public String getOriginatingServletPath(HttpServletRequest request) {
            return request.getRequestURI().substring(request.getContextPath().length());
        }
    }

And inject it to the Mapping Handler:

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="order" value="-1"></property>
        <property name="urlPathHelper">
            <bean class="com.yoochoose.frontend.spring.UrlPathHelperFixed"/>
        </property>
    </bean>

After a day of hard works it works now for me :-)

It was suggested to Spring team as https://jira.springsource.org/browse/SPR-11101

Sergey
  • 3,253
  • 2
  • 33
  • 55
30thh
  • 10,861
  • 6
  • 32
  • 42
  • 1
    Nice. It's fixed in Spring 3.2.8 according to the tkt. – arun Sep 03 '14 at 23:35
  • 1
    Using this fix still solves the issue for Spring 4.3.8; just setting urlDecode=false as suggested elsewhere doesn't seem to work correctly in all cases (although it does seem to work fine for some). – Jules Mar 01 '18 at 09:46
6

I have found this solution which is working for me;

System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");

just before springApplication.run(args);

and add below code in Application class

 @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setUrlDecode(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
Rich Benner
  • 7,873
  • 9
  • 33
  • 39
Ankur Gupta
  • 301
  • 4
  • 12
2

Spring Boot 3.x / Tomcat 10.x solution

Thanks to the comments from @leonid and @Edu, I managed to fix it for Tomcat 10.x, which removed the support for UDecoder.ALLOW_ENCODED_SLASH. They mention in the release notes:

Replace the system property org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH with the Connector attribute encodedSolidusHandling that adds an additional option to pass the %2f sequence through to the application without decoding it in addition to rejecting such sequences and decoding such sequences. (markt)

The Spring-equivalent of this is exactly what leonid and Edu pointed out:

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
        log.info("Configuring Tomcat to allow encoded slashes.");
        return factory -> factory.addConnectorCustomizers(connector -> connector.setEncodedSolidusHandling(
                EncodedSolidusHandling.DECODE.getValue()));
    }
Yigitalp Ertem
  • 1,901
  • 24
  • 27
1

We just ran into this issue at my office, we did what was suggestion above from what Solubris said where you put it in a query param. The only additional requirement is that the data could have an '&' as well, which would mess up the query param. All we had to do is encode the text before it is sent in the URL and even '&' were filtered out.

rsteier
  • 154
  • 2
  • 12
0

Another answer would be to encode "/" twice, which would produce "%252F".
In your mapped endpoint, Spring will decode it back to "%2F". All you need more is to decode it one more time using something like this:

URLDecoder.decode(encoded_URL, "UTF-8");

Amit Kumar Lal
  • 5,537
  • 3
  • 19
  • 37
0

The following resolved the BACK_SLASH issue:

public static void main(String[] args) throws Exception {
  System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
  SpringApplication.run(Application.class, args);
}

But, same functionality could be done via application.yml.

org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: true

This setting doesn't work. I did not find a way for that, and still looking at it.

  • To clarify, a "back slash" means the one that starts at the upper left and crosses down to the lower right, and a slash goes the other way. URLs have a SLASH (`https://www.stackoverflow.com`), Windows paths have a BACK_SLASH (`c:\windows`) for example. – Jim Jul 22 '20 at 15:11
0

In order to avoid parsing the variables manually I did the following:

  1. Add the following before executing any other code:
System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
  1. And in the controller, add 2 variables instead one, for example:
    @RequestMapping(value = "/api/devices-by-name/device={deviceId}/restconf/data/ietf-interfaces:interfaces-state/interface={dpuIdPrefix}/{dpuIdSuffix}",
            method = RequestMethod.GET,
            produces = "application/json")
    public ResponseEntity<String> getInterfaceState(@PathVariable(value = "deviceId") String deviceId,
                                                    @PathVariable(value = "dpuIdPrefix") String dpuIdPrefix,
                                                    @PathVariable(value = "dpuIdSuffix") String dpuIdSuffix) {
        String dpuId = dpuIdPrefix + "/" + dpuIdSuffix;

And with that I can retrieve the following:

curl -s -X GET http://localhost:9090/api/devices-by-name/device=ZNSDX16DPU03/restconf/data/ietf-interfaces:interfaces-state/interface=gfast%200%2F14

If the slash is optional, then you might need to configure two different request mappings.