@PathVariable containing backslash quote returns 400 Bad Request

Viewed 777

If you run this simple RestController with Spring Boot (2.5.3):

@RestController
public class SampleRestController {

    @GetMapping("/search/{criteria}")
    public String hello(@PathVariable(name = "criteria") String criteria) {
        return "Hello: " + criteria;
    }

}

And try to open this link in your browser:

 http://localhost:8080/search/%22%5C%22bug%5C%22%22 

Then you will get "400 Bad Request", returned by the embedded Tomcat.

I don't understand, is this a bug in Tomcat ? Is this not a valid URL ?

EDIT: As per some of the replies: I went step-by-step through the Tomcat 9.0.50 source-code and saw the line about ALLOW_BACKSLASH. And neither of the values true or false is good for me, because with true it replaced \ with / and with false it returns 400 Bad Request. What I needed was to allow backslash without replacing it with slash.

My question is really whether this is a bug in Tomcat, since for me the URL seems to be valid. I am not technically putting a \ into the URL, I am putting a %-encoded backslash. What is the purpose of the %-encoding if not to allow the user to send any character in the URL ?

3 Answers

Refer to Piotr P. Karwasz comments above for better explanation.

The tomcat version correspond to Spring Boot (2.5.3) is 9.0.50. By checking the source code of CoyoteAdaptor and Tomcat System parameter documentation, the url checking is configured by a flag ALLOW_BACKSLASH through System Property org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH, with false as default value.

...
    protected static final boolean ALLOW_BACKSLASH =
        Boolean.parseBoolean(System.getProperty("org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH", "false"));

...

To allow backslash in the URL, we can add below when running the application.

-Dorg.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH=true

This property is replaced after tomcat 10.0.0-M4.

Remove org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH system property, replaced by the allowBackslash attribute on the Connector. (remm)

Yes it seems like an invalid URL, you also get an error by Spring Boot refering to RFC 7230 and RFC 3986 specifications. But you could use some query parameter to avoid this error, e.g.:

@GetMapping("/search")
public String hello(@RequestParam(name = "criteria", required = false) String criteria) {
    return "Hello: " + criteria;
}

And call it like:

http://localhost:8080/search?criteria=%22%5C%22bug%5C%22%22 

While samabcde's answer provides a solution for your problem, let me answer to your more general questions:

  • your URL is perfectly valid although RFC 3986, section 7.3 allows some restriction to be imposed for security reasons,
  • these restrictions are not a Tomcat bug, as they were introduced as an answer to CVE-2007-0450. Before Tomcat 6.0.10 the sequences /, %2F and %5C were used to split the request URI into components. If Tomcat was behind a proxy that forwarded only an application (let's say /app), you could use /app/%5C../manager/html to access Tomcat Manager.

If you set org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH=true you expose your system to the aforementioned vulnerability and all %5C sequences will appear as / in the servletPath and pathInfo ServletRequest properties. Since the requestURI property contains the undecoded URI path (cf. this question) your example will work.

Related