tangguo

Spring 5.0.3 RequestRejectedException:由于网址未规范化,因此请求被拒绝

java

不知道这是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)

阅读 1165

收藏
2020-10-22

共2个答案

小编典典

Spring Security Documentation提到了阻塞请求的原因。

例如,它可能包含路径遍历序列(例如/../)或多个正斜杠(//),这也可能导致模式匹配失败。一些容器在执行servlet映射之前将它们标准化,但其他容器则没有。为了避免此类问题,FilterChainProxy使用HttpFirewall策略检查并包装请求。默认情况下,未规范化的请求将自动被拒绝,并且出于匹配目的,将删除路径参数和重复的斜杠。

因此,有两种可能的解决方案-

  1. 删除双斜杠(首选方法)
  2. 通过使用以下代码来自定义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仅需要声明一个类型为bean的bean HttpFirewall

2020-10-22
小编典典

我实现了一个StrictHttpFirewall将请求信息记录到控制台的子类,并抛出了一个带有抑制的堆栈跟踪的新异常。这部分解决了我的问题(至少现在我可以看到错误的请求)。

如果您只想查看没有堆栈跟踪的拒绝的请求,这就是您想要的答案。

如果要在控制器中处理这些异常,请参阅公认的答案以获取完整(但稍微复杂一点)的解决方案。

LoggingHttpFirewall.java

此类扩展了StrictHttpFirewall来捕获RequestRejectedException并引发新异常,该异常来自请求中的元数据,并且堆栈跟踪被抑制。

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.security.web.firewall.StrictHttpFirewall;

/**
 * Overrides the StrictHttpFirewall to log some useful information about blocked requests.
 */
public final class LoggingHttpFirewall extends StrictHttpFirewall
{
    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(LoggingHttpFirewall.class.getName());

    /**
     * Default constructor.
     */
    public LoggingHttpFirewall()
    {
        super();
        return;
    }

    /**
     * Provides the request object which will be passed through the filter chain.
     *
     * @returns A FirewalledRequest (required by the HttpFirewall interface) which
     *          inconveniently breaks the general contract of ServletFilter because
     *          we can't upcast this to an HttpServletRequest. This prevents us
     *          from re-wrapping this using an HttpServletRequestWrapper.
     * @throws RequestRejectedException if the request should be rejected immediately.
     */
    @Override
    public FirewalledRequest getFirewalledRequest(final HttpServletRequest request) throws RequestRejectedException
    {
        try
        {
            return super.getFirewalledRequest(request);
        } catch (RequestRejectedException ex) {
            if (LOGGER.isLoggable(Level.WARNING))
            {
                LOGGER.log(Level.WARNING, "Intercepted RequestBlockedException: Remote Host: " + request.getRemoteHost() + " User Agent: " + request.getHeader("User-Agent") + " Request URL: " + request.getRequestURL().toString());
            }

            // Wrap in a new RequestRejectedException with request metadata and a shallower stack trace.
            throw new RequestRejectedException(ex.getMessage() + ".\n Remote Host: " + request.getRemoteHost() + "\n User Agent: " + request.getHeader("User-Agent") + "\n Request URL: " + request.getRequestURL().toString())
            {
                private static final long serialVersionUID = 1L;

                @Override
                public synchronized Throwable fillInStackTrace()
                {
                    return this; // suppress the stack trace.
                }
            };
        }
    }

    /**
     * Provides the response which will be passed through the filter chain.
     * This method isn't extensible because the request may already be committed.
     * Furthermore, this is only invoked for requests that were not blocked, so we can't
     * control the status or response for blocked requests here.
     *
     * @param response The original HttpServletResponse.
     * @return the original response or a replacement/wrapper.
     */
    @Override
    public HttpServletResponse getFirewalledResponse(final HttpServletResponse response)
    {
        // Note: The FirewalledResponse class is not accessible outside the package.
        return super.getFirewalledResponse(response);
    }
}

WebSecurityConfig.java

在中WebSecurityConfig,将HTTP防火墙设置为LoggingHttpFirewall。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
    /**
     * Default constructor.
     */
    public WebSecurityConfig()
    {
        super();
        return;
    }

    @Override
    public final void configure(final WebSecurity web) throws Exception
    {
        super.configure(web);
        web.httpFirewall(new LoggingHttpFirewall()); // Set the custom firewall.
        return;
    }
}

结果

在将该解决方案部署到生产环境后,我很快发现的默认行为StrictHttpFirewall是阻止Google将我的网站编入索引!

Aug 13, 2018 1:48:56 PM com.mycompany.spring.security.AnnotatingHttpFirewall getFirewalledRequest
WARNING: Intercepted RequestBlockedException: Remote Host: 66.249.64.223 User Agent: Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) Request URL: https://www.mycompany.com/10.1601/tx.3784;jsessionid=692804549F9AB55F45DBD0AFE2A97FFD

一旦发现这一点,我便迅速部署了一个新版本(包含在我的其他答案中)来查找;jsessionid=并允许这些请求通过。可能还有其他一些请求也应该通过,现在我可以检测到这些请求。

2020-10-22