如果您使用 Spring Boot (2.5.3) 运行这个简单的 RestController:
@RestController
public class SampleRestController {
@GetMapping("/search/{criteria}")
public String hello(@PathVariable(name = "criteria") String criteria) {
return "Hello: " + criteria;
}
}
并尝试在浏览器中打开此链接:
http://localhost:8080/search/%22%5C%22bug%5C%22%22
然后您将收到“400 Bad Request”,由嵌入式 Tomcat 返回。
我不明白,这是 Tomcat 中的错误吗?这不是一个有效的网址吗?
编辑:根据一些回复:我一步步浏览了 Tomcat 9.0.50 源代码,看到了有关 ALLOW_BACKSLASH 的行。 true 或 false 值对我来说都没有好处,因为使用 true 时,它会用
\
替换 /
,使用 false 时,它会返回 400 Bad Request。
我需要的是允许反斜杠而不用斜杠替换它。
我的问题实际上是这是否是 Tomcat 中的一个错误,因为对我来说该 URL 似乎是有效的。从技术上讲,我并没有在 URL 中放入
\
,而是放入了 % 编码的反斜杠。如果不允许用户在 URL 中发送任何字符,那么 % 编码的目的是什么?
Spring Boot(2.5.3)对应的tomcat版本是9.0.50。
通过查看CoyoteAdaptor
ALLOW_BACKSLASH
通过标志
org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH
配置的,默认值为
false
。
...
protected static final boolean ALLOW_BACKSLASH =
Boolean.parseBoolean(System.getProperty("org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH", "false"));
...
要允许在 URL 中使用反斜杠,我们可以在运行应用程序时添加以下内容。
-Dorg.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH=true
此属性在 tomcat 10.0.0-M4 后被替换。
删除 org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH 系统属性,替换为连接器上的allowBackslash 属性。 (雷姆)
samabcde的回答为您的问题提供了解决方案,但让我回答您更一般的问题:
/
、
%2F
和
%5C
将请求 URI 拆分为组件。如果 Tomcat 位于仅转发应用程序的代理后面(假设
/app
),您可以使用
/app/%5C../manager/html
访问 Tomcat 管理器。
org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH=true
,您的系统就会暴露于上述漏洞,并且所有
%5C
序列将在
/
和
servletPath
pathInfo
属性中显示为
ServletRequest
。由于
requestURI
属性包含未解码的 URI 路径(参见这个问题),您的示例将起作用。
@GetMapping("/search")
public String hello(@RequestParam(name = "criteria", required = false) String criteria) {
return "Hello: " + criteria;
}
并称其为:
http://localhost:8080/search?criteria=%22%5C%22bug%5C%22%22
使用Spring boot 3.2.4版本10.x.x。假设路径变量中包含 %2F 或 %5C 并收到 400 Bad request。 我已经尝试过上述方法,但不知何故它对我不起作用。感谢这篇
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer()
{
return factory -> factory.addConnectorCustomizers(connector -> {
connector.setAllowBackslash(true);
connector.setEncodedSolidusHandling(
EncodedSolidusHandling.DECODE.getValue());
});
}