Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter out null WebSocket session attributes #29315

Closed
evil0th opened this issue Oct 13, 2022 · 0 comments
Closed

Filter out null WebSocket session attributes #29315

evil0th opened this issue Oct 13, 2022 · 0 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@evil0th
Copy link

evil0th commented Oct 13, 2022

Affects: <Spring Framework version 5.3.22, 5.3.23, maybe earlier>


image

image

my codes

step 0(optional): prepare the spring boot project, add dependency: org.springframework.boot:spring-boot-starter-websocket

step 1: custom HandshakeInterceptor

@Component
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
        map.put("uid", null);// null value not controlled from some logic
        return true;
    }
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
    }
}

step2: WebSocketConfigurer

@EnableWebSocket
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
        webSocketHandlerRegistry.addHandler(new TextWebSocketHandler(), "/ws/**")
                .addInterceptors(new WebSocketHandshakeInterceptor())
                .setAllowedOrigins("*");
    }
}
step3: client html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocker Client</title>
</head>
<body>
<button onclick="contactServer">Click Here</button>
</body>
<script>
    const socket = new WebSocket('ws://localhost:8080/ws');
    socket.addEventListener('open', function (event) {
        socket.send('Connection Established');
    });

    socket.addEventListener('message', function (event) {
        console.log(event.data);
    });

    const contactServer = () => {
        socket.send("Initialize");
    }
</script>
</html>

error log

nested exception is org.springframework.web.socket.server.HandshakeFailureException: Uncaught failure for request http://localhost:8080/ws; nested exception is java.lang.NullPointerException] with root cause

------- origin log -------
2022-10-13 14:57:21.509 DEBUG 8271 --- [nio-8080-exec-1] o.s.w.s.s.s.WebSocketHttpRequestHandler  : GET /ws
2022-10-13 14:57:21.535 TRACE 8271 --- [nio-8080-exec-1] o.s.w.s.s.s.DefaultHandshakeHandler      : Processing request http://localhost:8080/ws with headers=[host:"localhost:8080", connection:"Upgrade", pragma:"no-cache", cache-control:"no-cache", user-agent:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", upgrade:"websocket", origin:"null", sec-websocket-version:"13", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,ko;q=0.8,ja;q=0.7,en;q=0.6", sec-websocket-key:"egMr7FKax7k2Vtkgcv8Ksw==", sec-websocket-extensions:"permessage-deflate; client_max_window_bits"]
2022-10-13 14:57:21.537 TRACE 8271 --- [nio-8080-exec-1] o.s.w.s.s.s.DefaultHandshakeHandler      : Upgrading to WebSocket, subProtocol=null, extensions=[]
2022-10-13 14:57:24.156 ERROR 8271 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.socket.server.HandshakeFailureException: Uncaught failure for request http://localhost:8080/ws; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
	at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) ~[na:na]
	at java.base/java.util.concurrent.ConcurrentHashMap.putAll(ConcurrentHashMap.java:1089) ~[na:na]
	at org.springframework.web.socket.adapter.AbstractWebSocketSession.<init>(AbstractWebSocketSession.java:65) ~[spring-websocket-5.3.23.jar:5.3.23]
	at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.<init>(StandardWebSocketSession.java:104) ~[spring-websocket-5.3.23.jar:5.3.23]
	at org.springframework.web.socket.server.standard.AbstractStandardUpgradeStrategy.upgrade(AbstractStandardUpgradeStrategy.java:129) ~[spring-websocket-5.3.23.jar:5.3.23]
	at org.springframework.web.socket.server.support.AbstractHandshakeHandler.doHandshake(AbstractHandshakeHandler.java:297) ~[spring-websocket-5.3.23.jar:5.3.23]
	at org.springframework.web.socket.server.support.WebSocketHttpRequestHandler.handleRequest(WebSocketHttpRequestHandler.java:178) ~[spring-websocket-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:52) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
	at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

tracking & analysis

found code call path:
AbstractStandardUpgradeStrategy#upgrade->
StandardWebSocketSession session = new StandardWebSocketSession(headers, attrs, localAddr, remoteAddr, user);
->StandardWebSocketSession#constructor->first line super(attributes);

The code where the exception finally occurs:

// org.springframework.web.socket.adapter.AbstractWebSocketSession.java
public AbstractWebSocketSession(@Nullable Map<String, Object> attributes) {
	if (attributes != null) {
		this.attributes.putAll(attributes); // <----- HERE, java.util.concurrent.ConcurrentHashMap put null value occurs NPE
	}
}

It is recommended to add a null value judgment and throw an exception or filter none-null key-value here。

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Oct 13, 2022
@sbrannen sbrannen added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Oct 13, 2022
@sbrannen sbrannen added this to the Triage Queue milestone Oct 13, 2022
@rstoyanchev rstoyanchev changed the title Meet NPE when accidentally set a null value to map in a override beforeHandshake method NullPointerException if beforeHandshake adds attribute with null value Oct 24, 2022
@rstoyanchev rstoyanchev self-assigned this Oct 24, 2022
@rstoyanchev rstoyanchev modified the milestones: Triage Queue, 5.3.24 Oct 24, 2022
@rstoyanchev rstoyanchev added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Oct 24, 2022
@rstoyanchev rstoyanchev changed the title NullPointerException if beforeHandshake adds attribute with null value Reject null keys/values added to WebSocket session attributes in beforeHandshake Oct 24, 2022
@rstoyanchev rstoyanchev changed the title Reject null keys/values added to WebSocket session attributes in beforeHandshake Filter out null WebSocket session attributes Nov 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants