Spring 5.0.3 RequestRejectedException:请求被拒绝,因为URL未规范化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 5.0.3 RequestRejectedException:请求被拒绝,因为URL未规范化相关的知识,希望对你有一定的参考价值。
不确定这是否是Spring 5.0.3的一个错误或一个新功能来解决我的问题。
升级后,我收到此错误。有趣的是,此错误仅在我的本地计算机上。使用HTTPS协议的测试环境上的相同代码工作正常。
继续...
我收到此错误的原因是因为我加载生成的JSP页面的URL是/location/thisPage.jsp
。评估代码request.getRequestURI()
给了我结果/WEB-INF/somelocation//location/thisPage.jsp
。如果我将JSP页面的URL修复到这个location/thisPage.jsp
,那么事情就可以了。
所以我的问题是,我应该从代码中的/
路径中删除JSP
,因为这是未来需要的。或者Spring
引入了一个错误,因为我的机器和测试环境之间的唯一区别是协议HTTP
与HTTPS
。
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL was not normalized.
at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:123)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:194)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
Spring Security Documentation在请求中提到阻止//的原因。
例如,它可能包含路径遍历序列(如/../)或多个正斜杠(//),这也可能导致模式匹配失败。一些容器在执行servlet映射之前将这些规范化,但其他容器则没有。为了防止这些问题,FilterChainProxy使用HttpFirewall策略来检查和包装请求。默认情况下会自动拒绝未规范化的请求,并且会删除路径参数和重复斜杠以进行匹配。
所以有两种可能的解决方案 -
- 删除双斜线(首选方法)
- 通过使用以下代码自定义StrictHttpFirewall,在Spring Security中允许//。
步骤1创建允许在URL中使用斜杠的自定义防火墙。
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
步骤2然后在websecurity中配置此bean
@Override
public void configure(WebSecurity web) throws Exception {
//@formatter:off
super.configure(web);
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
....
}
第2步是可选步骤,Spring Boot只需要声明类型为HttpFirewall
的bean
setAllowUrlEncodedSlash(true)对我不起作用。仍然内部方法isNormalized具有双斜线时返回false。
我通过仅使用以下代码将DefaultTttHirepFirewall替换为DefaultHttpFirewall。
@Bean
public HttpFirewall defaultHttpFirewall() {
return new DefaultHttpFirewall();
}
对我有用。使用DefaultHttpFirewall有任何风险吗?
我遇到了同样的问题:
Spring Boot版本= 1.5.10 Spring Security版本= 4.2.4
问题发生在端点上,其中ModelAndView
viewName是使用前面的正斜杠定义的。例:
ModelAndView mav = new ModelAndView("/your-view-here");
如果我删除斜线它工作正常。例:
ModelAndView mav = new ModelAndView("your-view-here");
我还使用RedirectView进行了一些测试,它似乎与前面的正斜杠一起工作。
下面的解决方案是一个干净的解决方案。它不会危及安全性,因为我们使用相同的严格防火墙。
修复步骤如下:
第1步:创建一个覆盖StrictHttpFirewall的类,如下所示。
package com.biz.brains.project.security.firewall;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedException;
public class CustomStrictHttpFirewall implements HttpFirewall {
private static final Set<String> ALLOW_ANY_HTTP_METHOD = Collections.unmodifiableSet(Collections.emptySet());
private static final String ENCODED_PERCENT = "%25";
private static final String PERCENT = "%";
private static final List<String> FORBIDDEN_ENCODED_PERIOD = Collections.unmodifiableList(Arrays.asList("%2e", "%2E"));
private static final List<String> FORBIDDEN_SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));
private static final List<String> FORBIDDEN_FORWARDSLASH = Collections.unmodifiableList(Arrays.asList("%2f", "%2F"));
private static final List<String> FORBIDDEN_BACKSLASH = Collections.unmodifiableList(Arrays.asList("\", "%5c", "%5C"));
private Set<String> encodedUrlBlacklist = new HashSet<String>();
private Set<String> decodedUrlBlacklist = new HashSet<String>();
private Set<String> allowedHttpMethods = createDefaultAllowedHttpMethods();
public CustomStrictHttpFirewall() {
urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
urlBlacklistsAddAll(FORBIDDEN_BACKSLASH);
this.encodedUrlBlacklist.add(ENCODED_PERCENT);
this.encodedUrlBlacklist.addAll(FORBIDDEN_ENCODED_PERIOD);
this.decodedUrlBlacklist.add(PERCENT);
}
public void setUnsafeAllowAnyHttpMethod(boolean unsafeAllowAnyHttpMethod) {
this.allowedHttpMethods = unsafeAllowAnyHttpMethod ? ALLOW_ANY_HTTP_METHOD : createDefaultAllowedHttpMethods();
}
public void setAllowedHttpMethods(Collection<String> allowedHttpMethods) {
if (allowedHttpMethods == null) {
throw new IllegalArgumentException("allowedHttpMethods cannot be null");
}
if (allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {
this.allowedHttpMethods = ALLOW_ANY_HTTP_METHOD;
} else {
this.allowedHttpMethods = new HashSet<>(allowedHttpMethods);
}
}
public void setAllowSemicolon(boolean allowSemicolon) {
if (allowSemicolon) {
urlBlacklistsRemoveAll(FORBIDDEN_SEMICOLON);
} else {
urlBlacklistsAddAll(FORBIDDEN_SEMICOLON);
}
}
public void setAllowUrlEncodedSlash(boolean allowUrlEncodedSlash) {
if (allowUrlEncodedSlash) {
urlBlacklistsRemoveAll(FORBIDDEN_FORWARDSLASH);
} else {
urlBlacklistsAddAll(FORBIDDEN_FORWARDSLASH);
}
}
public void setAllowUrlEncodedPeriod(boolean allowUrlEncodedPeriod) {
if (allowUrlEncodedPeriod) {
this.encodedUrlBlacklist.removeAll(FORBIDDEN_ENCODED_PERIOD);
} else {
this.encodedUrlBlacklist.addAll(FORBIDDEN_ENCODED_PERIOD);
}
}
public void setAllowBackSlash(boolean allowBackSlash) {
if (allowBackSlash) {
urlBlacklistsRemoveAll(FORBIDDEN_BACKSLASH);
} else {
urlBlacklistsAddAll(FORBIDDEN_BACKSLASH);
}
}
public void setAllowUrlEncodedPercent(boolean allowUrlEncodedPercent) {
if (allowUrlEncodedPercent) {
this.encodedUrlBlacklist.remove(ENCODED_PERCENT);
this.decodedUrlBlacklist.remove(PERCENT);
} else {
this.encodedUrlBlacklist.add(ENCODED_PERCENT);
this.decodedUrlBlacklist.add(PERCENT);
}
}
private void urlBlacklistsAddAll(Collection<String> values) {
this.encodedUrlBlacklist.addAll(values);
this.decodedUrlBlacklist.addAll(values);
}
private void urlBlacklistsRemoveAll(Collection<String> values) {
this.encodedUrlBlacklist.removeAll(values);
this.decodedUrlBlacklist.removeAll(values);
}
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
rejectForbiddenHttpMethod(request);
rejectedBlacklistedUrls(request);
if (!isNormalized(request)) {
request.setAttribute("isNormalized", new RequestRejectedException("The request was rejected because the URL was not normalized."));
}
String requestUri = request.getRequestURI();
if (!containsOnlyPrintableAsciiCharacters(requestUri)) {
request.setAttribute("isNormalized", new RequestRejectedException("The requestURI was rejected because it can only contain printable ASCII characters."));
}
return new FirewalledRequest(request) {
@Override
public void reset() {
}
};
}
private void rejectForbiddenHttpMethod(HttpServletRequest request) {
if (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {
return;
}
if (!this.allowedHttpMethods.contains(request.getMethod())) {
request.setAttribute("isNormalized", new RequestRejectedException("The request was rejected because the HTTP method "" +
request.getMethod() +
"" was not included within the whitelist " +
this.allowedHttpMethods));
}
}
private void rejectedBlacklistedUrls(HttpServletRequest request) {
fo以上是关于Spring 5.0.3 RequestRejectedException:请求被拒绝,因为URL未规范化的主要内容,如果未能解决你的问题,请参考以下文章
SpringSecurity---javaconfig:Hello Web Security
centos / Linux 服务环境下安装 Redis 5.0.3
@angular/cdk@5.0.3 需要 @angular/common@~5.1.1 的对等体,但没有安装