我在Tomcat日志中看到 * 大量 * RequestRejectedException
条目(示例粘贴在下面)。这些开始出现在我的日志文件后,一个次要版本升级(Spring Security 4.2.4,IIRC),所以这显然是Spring中默认启用的一个新的安全特性。一个类似的问题是reported here,但我的问题特别涉及到如何在控制器中拦截这些异常。有一个Spring Security bug记录了这个问题(Provide a way to handle RequestRejectedException)。然而,他们直到Spring 5.1才针对这个问题进行修复。
我理解why these exceptions are being thrown,我不想disable this security feature。
我想获得对此功能的一些控制权,以便:
1.我知道我没有阻止合法用户访问我的网站。
1.我可以看到是什么请求触发了这种情况(它们是SQL注入攻击吗?)
1.我可以调整服务器响应。Spring Security防火墙将一个完整的堆栈跟踪转储到Web客户端(信息泄漏),沿着一个500 Internal Server Error
(这是非常不正确的,这应该是一个400 Bad Request
)。
我想找到一种方法来记录所请求的URL,但也要抑制专门针对这些异常的堆栈跟踪,因为它们污染了我的日志文件,却没有提供任何有用的信息。最理想的情况是,我想拦截这些异常并在应用程序层处理它们,而不是在Tomcat日志中报告它们。
例如,这是我的catalina.out
中每天出现的数千条日志条目之一:
Aug 10, 2018 2:01:36 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [] threw exception
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:265)
at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:245)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:193)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:486)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
我在两天内看到了3,200多个这样的问题,它很快就成为我的catalina.out
日志文件的最大贡献者,以至于它阻止了我看到其他合法的问题。而且从四月份开始它已经浪费了我几个小时的时间,我并不是说它不是一个重要的特性,只是默认的实现完全搞砸了,我想找到一种方法来获得对它的一些控制权,作为开发人员和系统管理员。
我在Spring中使用了一个自定义的错误控制器来拦截许多其他的异常类型(包括IOException
),但是RequestRejectedException
似乎由于某种原因而失败了。
这是我的ErrorController.java
的相关部分,给予我们了解一下我想要完成的任务:
@ControllerAdvice
public final class ErrorController
{
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(ErrorController.class.getName());
/**
* Generates an Error page by intercepting exceptions generated from HttpFirewall.
*
* @param ex A RequestRejectedException exception.
* @return The tile definition name for the page.
*/
@ExceptionHandler(RequestRejectedException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleRequestRejectedException(final HttpServletRequest request, final RequestRejectedException ex)
{
if (LOGGER.isLoggable(Level.INFO))
{
LOGGER.log(Level.INFO, "Request Rejected", ex);
}
LOGGER.log(Level.WARNING, "Rejected request for [" + request.getRequestURL().toString() + "]. Reason: " + ex.getMessage());
return "errorPage";
}
/**
* Generates a Server Error page.
*
* @param ex An exception.
* @return The tile definition name for the page.
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleException(final Exception ex)
{
if (LOGGER.isLoggable(Level.SEVERE))
{
LOGGER.log(Level.SEVERE, "Server Error", ex);
}
return "errorPage";
}
}
此错误控制器适用于许多异常。例如,它成功地拦截了以下IllegalStateException
:
Aug 05, 2018 7:50:30 AM com.mycompany.spring.controller.ErrorController handleException
SEVERE: Server Error
java.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:2999)
...
但是,这不会拦截RequestRejectedException
(如上面第一个日志示例中缺少“Server Error”所示)。
如何在错误控制器中拦截RequestRejectedException
?
9条答案
按热度按时间kqlmhetl1#
它也可以由一个简单的滤波器处理,这将导致404错误响应
tkclm6bt2#
对于Spring安全性版本
5.4
和更高版本,您可以简单地创建一个类型为RequestRejectedHandler
的bean,该bean将被注入到Spring安全性过滤器链中uqxowvwt3#
我实现了
StrictHttpFirewall
的一个子类,它将请求信息记录到控制台,并抛出一个新的异常,并抑制堆栈跟踪,这部分地解决了我的问题(至少我现在可以看到坏的请求)。如果您只想看到被拒绝的请求而不想看到堆栈跟踪,那么这就是您要寻找的答案。
如果您想在控制器中处理这些异常,请参考accepted answer以获得完整(但稍微复杂一些)的解决方案。
此类扩展StrictHttpFirewall以捕获
RequestRejectedException
,并引发一个新的异常,其中包含来自请求的元数据和隐藏的堆栈跟踪。在
WebSecurityConfig
中,将HTTP防火墙设置为LoggingHttpFirewall
。在将这个解决方案部署到生产环境中之后,我很快发现
StrictHttpFirewall
的默认行为是阻止Google索引我的站点!当我发现这一点时,我很快部署了一个新版本(包含在my other answer中),它查找
;jsessionid=
并允许这些请求通过,很可能还有其他请求也应该通过,现在我有了检测这些请求的方法。ffx8fchx4#
结果表明,尽管
HttpFirewall
和StrictHttpFirewall
包含几个设计错误(记录在下面的代码中),几乎不可能逃脱Spring Security的 * One True Firewall ,并通过request属性将HttpFirewall
信息传送到HandlerInterceptor
,HandlerInterceptor
可以将这些标记的请求传递到 * Real (持久的)防火墙,而不牺牲最初标记它们的原始业务逻辑。这里所描述的方法应该是经得起未来考验的,因为它符合来自HttpFirewall
接口的简单约定,剩下的就是核心Spring框架和JavaServlet API。这实际上是my earlier answer的一个更复杂但更完整的替代方案。在这个答案中,我实现了
StrictHttpFirewall
的一个新子类,它在特定的日志记录级别拦截和记录被拒绝的请求,但也向HTTP请求添加了一个属性,用于标记它以供下游过滤器使用此外,这个AnnotatingHttpFirewall
提供了一个inspect()
方法,允许子类添加阻塞请求的自定义规则。此解决方案分为两部分:(1) Spring Security * 和(2) Spring Framework(核心)*,因为这是首先导致此问题的分歧,这显示了如何弥合它。
作为参考,这在Spring 4. 3. 17和Spring Security 4. 2. 6上进行了测试。Spring 5. 1发布时可能会有重大变化。
这是在Spring Security中执行日志记录和标记的解决方案的一半。
在
WebSecurityConfig
中,将HTTP防火墙设置为AnnotatingHttpFirewall
。这个解决方案的第二部分可以实现为
ServletFilter
或HandlerInterceptor
,我选择HandlerInterceptor
,因为它似乎提供了最大的灵活性,并且可以直接在Spring框架中工作。此自定义异常可由错误控制器处理。这可被扩展以添加可从原始请求(甚至完整请求本身)获得的可能与应用业务逻辑(例如,持久防火墙)相关的任何请求标头、参数或属性。
这个拦截器在Spring Security过滤器运行之后被调用(例如,在
AnnotatingHttpFirewall
标记了应该被拒绝的请求之后)。这个拦截器检测请求上的那些标记(属性),并引发我们的错误控制器可以处理的自定义异常。在
WebConfig
中,将FirewallInterceptor
添加到注册表中。这专门处理上面的自定义异常,并为客户端生成一个干净的错误页面,同时记录所有相关信息,并为自定义应用程序防火墙调用任何特殊的业务逻辑。
一个具有默认Map的控制器,它抛出一个
NoHandlerFoundException
。这绕过了DispatcherServlet.noHandlerFound中的先有鸡还是先有蛋的策略,允许该方法 * 总是 * 找到一个Map,以便总是调用FirewallInterceptor.preHandle
。这使RequestRejectedByFirewallException
优先于NoHandlerFoundException
。为什么需要这样做:
如前所述,当从
DispatcherServlet
抛出NoHandlerFoundException
时(即,当请求的URL没有对应的Map时),无法处理从上述防火墙生成的异常(在调用preHandle()之前抛出NoHandlerFoundException
),所以这些请求会被你的404浏览器拒绝(在我的例子中,这并不是所期望的行为--您将看到许多"找不到带URI的HTTP请求的Map......"消息)。这可以通过将特殊头的检查移到noHandlerFound
方法中来解决。不幸的是,如果不从头开始编写一个新的DispatcherServlet,就没有办法做到这一点,然后您还不如扔掉整个Spring框架。由于protected、private和final方法,以及它的属性不可访问(没有getter或setter)的事实。 Package 类也是不可能的,因为没有可以实现的公共接口。该类中的默认Map提供了一种优雅的方式来绕过所有这些逻辑。当这两个部分都正常工作时,您将看到以下两个警告记录(第一个在Spring Security中,第二个在Spring Framework(Core)
ErrorController
中)。现在您可以完全控制日志记录,并且可以根据需要调整可扩展的应用程序防火墙。6ovsh4lw5#
另一种处理方法是使用SpringAOP,我们可以创建一个关于FilterChainProxy.doFilter()方法的通知,该方法捕获HttpFirewall抛出的任何RequestRejectedException(s),并将其转换为400BAD_REQUEST
bis0qfac6#
我在最近的github变更中看到了一些可行的解决方案
如果你注册了
RequestRejectedHandler
类型的bean,它应该可以工作,或者正如我所看到的,在WebSecurityConfigurerAdapter
中也会有一个通过WebSecurity
的集成。不幸的是,这个更改可能没有包含在使用依赖管理的2.3.3.RELEASE中。它应该出现在Spring Security Config 5.4.0-M1中。对于依赖管理,它是版本2.4.0-M1。迟早,遇到这个答案的人应该会在标准版本中看到这个变化。
xoshrz7s7#
在springsecurity5.7.6中,我可以使用此代码记录错误并重定向到404页面
hrysbysz8#
一种非常简单的方法是使用web.xml;在文件中指定错误页:
对于指定的路径(位置),在
@Controller
注解的类中添加一个Map:v09wglhw9#
我们的问题是使用spring-security-core(4.2.13.RELEASE)的spring-webmvc(4.3.25.RELEASE)GUI,该问题是由于url字符串包含
";jsessionid=D3A0470674704B75756AA10F50AA2CFC"
,其中一个参数为分号。错误
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
用于只发生在网页加载第一次导致各种CSS格式的问题,图像没有加载,颜色和字体设置不正确。然而,刷新同一页面或点击任何导航链接后,下一页用于加载罚款与所有CSS实现正确。此外,这个错误RequestRejectedException
严重污染了日志。我们希望处理这个问题,以便在创建新会话时,生成的cookie使用jsessionid处理会话,而不是像第二次那样处理查询字符串。
我的解决方案是通过在https://stackoverflow.com/a/52635656/2915705中实现上面描述的过滤器而得到的,但不是将其发送到会话的错误页面检查并将其重定向到https://stackoverflow.com/a/4019476/2915705中描述的编码URL。在此解决方案之后,我们从未收到
RequestRejectedException
或CSS问题,即使是新会话或第一次加载页面。更新后的
LogAndSuppressRequestRejectedExceptionFilter
如下所示