站点资源在 GetMapping 中泄漏到 UUID

Posted

技术标签:

【中文标题】站点资源在 GetMapping 中泄漏到 UUID【英文标题】:Site resources leaks into UUID in GetMapping 【发布时间】:2018-08-01 19:14:45 【问题描述】:

我对这个问题进行了很多研究,但没有找到任何人提到类似的事情。

我正在创建一个 Spring Boot 应用程序并使用 GetMapping 来映射我的资源。出于某种原因,当我使用 UUID 作为我的 PathVariable 时,我遇到了奇怪的错误。

Controller.java

@GetMapping("/test/id1")
public String testMethod(@PathVariable UUID id1)
    return "test";
    

Request:
/test/b74adb59-af60-423e-a3dd-c2f356114b51

test.html - 带有不存在的样式表。

<html>
    <head>
        <link rel="stylesheet" href="bullshit">
    </head>
    <body>
        <h1>This is a test page</h1>
    </body>
</html>

这将导致以下错误:

org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.UUID'; nested exception is java.lang.IllegalArgumentException: Invalid UUID string: bullshit
2018-02-22 03:49:23.062  WARN 252700 --- [nio-8080-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.UUID'; nested exception is java.lang.IllegalArgumentException: Invalid UUID string: bullshit
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:128)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:160)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:121)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException: Invalid UUID string: bullshit
    at java.util.UUID.fromString(UUID.java:194)
    at org.springframework.beans.propertyeditors.UUIDEditor.setAsText(UUIDEditor.java:37)
    at org.springframework.beans.TypeConverterDelegate.doConvertTextValue(TypeConverterDelegate.java:468)
    at org.springframework.beans.TypeConverterDelegate.doConvertValue(TypeConverterDelegate.java:441)
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:199)
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:108)
    at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64)
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:47)
    at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:713)
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:120)
    ... 83 more

test.html - 如果我使用有效的 css 文件,我不会收到错误

<html>
    <head>
        <link rel="stylesheet" href="/path/valid.css">
    </head>
    <body>
        <h1>This is a test page</h1>
    </body>
</html>

Controller.java - 如果 UUID 后跟斜杠 / 和无效的 CSS 文件,那么我不会收到任何错误。

@GetMapping("/test/id1/")
public String testMethod(@PathVariable UUID id1)
    return "test";


Request:
/test/b74adb59-af60-423e-a3dd-c2f356114b51/

<html>
    <head>
        <link rel="stylesheet" href="bullshit">
    </head>
    <body>
        <h1>This is a test page</h1>
    </body>
</html>

我似乎无效的 CSS 泄漏到第二个 UUID 中。我知道我可以通过不使用无效的 CSS 路径来简单地解决这个问题,但我很想明白为什么会出现这种错误。

编辑: 我创建了一个简单的测试应用程序来展示错误。这可能使您更容易修补它。将项目作为 Spring Boot 应用程序运行并访问 localhost:8080 - 请记住注意您的控制台。

https://github.com/kkflf/SpringBootUuidWarning

【问题讨论】:

【参考方案1】:

这是由使用相对样式表 URL href="bullshit" 引起的。浏览器将使用网站 URL 解析它,主要区别在于结尾的 / 字符。

对于http://localhost/test/b74adb59-af60-423e-a3dd-c2f356114b51,浏览器将请求http://localhost/test/bullshit 在@Controller 中点击@GetMapping。这将导致 Spring 无法将 bullshit String 转换为有效的 UUID,并且将抛出 IllegalArgumentException。

对于http://localhost/test/b74adb59-af60-423e-a3dd-c2f356114b51/,浏览器将请求http://localhost/test/b74adb59-af60-423e-a3dd-c2f356114b51/bullshit,这将由ResourceHttpRequestHandler 处理,由于没有test/b74adb59-af60-423e-a3dd-c2f356114b51/bullshit 资源,因此将返回404。在 Spring Boot 启动期间,/** URL 被映射到 ResourceHttpRequestHandler 日志。

【讨论】:

感谢您的回答。我已经给你赏金了,甚至另一个人在你之前回答了它。你的回答比较详细。我的问题相当简单明了,在我发布赏金后我确实设法弄清楚了。【参考方案2】:

因为 method1 url 以 / 结尾,所以链接 href 将 style url 解析为 /method1/b74adb59-af60-423e-a3dd-c2f356114b51/bs,其中包含正确的 UUID 并且 Spring Boot 能够将该字符串映射到UUID 格式,而method2 url 不以/ 结尾,这使得浏览器将链接href 视为/method2/bs,并且由于“bs”不是有效的UUID,因此会引发IllegalArgumentException。

【讨论】:

以上是关于站点资源在 GetMapping 中泄漏到 UUID的主要内容,如果未能解决你的问题,请参考以下文章

XSLeaks 攻击分析 —— HTTP缓存跨站点泄漏

cppcheck 检测到资源泄漏 - 误报?

内存泄漏

DrawFrameControl 中的 GDI 资源泄漏

cppcheck 未检测到来自 epoll_create 的资源泄漏

由于包含路径,cppcheck 未检测到资源泄漏