diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java index 429e185b3e..4deccb35a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java @@ -15,14 +15,20 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import lombok.extern.slf4j.Slf4j; import org.springframework.util.SerializationUtils; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; import java.util.Base64; import java.util.Optional; +@Slf4j public class CookieUtils { public static Optional getCookie(HttpServletRequest request, String name) { @@ -67,7 +73,22 @@ public class CookieUtils { } public static T deserialize(Cookie cookie, Class cls) { - return cls.cast(SerializationUtils.deserialize( - Base64.getUrlDecoder().decode(cookie.getValue()))); + byte[] decodedBytes = Base64.getUrlDecoder().decode(cookie.getValue()); + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decodedBytes)) { + @Override + protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { + String name = desc.getName(); + if (!cls.getName().equals(name)) { + throw new ClassNotFoundException("Class not allowed for deserialization: " + name); + } + return super.resolveClass(desc); + } + }) { + + return cls.cast(ois.readObject()); + } catch (Exception e) { + log.debug("Failed to deserialize class from cookie.", e.getCause()); + return null; + } } } diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepositoryTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepositoryTest.java new file mode 100644 index 0000000000..3691952b22 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepositoryTest.java @@ -0,0 +1,66 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.security.auth.oauth2; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +import static org.junit.Assert.assertEquals; +import static org.thingsboard.server.service.security.auth.oauth2.HttpCookieOAuth2AuthorizationRequestRepository.OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME; + +public class HttpCookieOAuth2AuthorizationRequestRepositoryTest { + + private static final String SERIALIZED_ATTACK_STRING = + "rO0ABXNyAHVvcmcudGhpbmdzYm9hcmQuc2VydmVyLnNlcnZpY2Uuc2VjdXJpdHkuYXV0aC5vYXV0aDIuSHR0cENvb2tpZU9BdXRoMkF1dGhvcml6YXRpb25SZXF1ZXN0UmVwb3NpdG9yeVRlc3QkTWFsaWNpb3VzQ2xhc3MAAAAAAAAAAAIAAHhw"; + + private static int maliciousMethodInvocationCounter; + + @Before + public void resetInvocationCounter() { + maliciousMethodInvocationCounter = 0; + } + + @Test + public void whenLoadAuthorizationRequest_thenMaliciousMethodNotInvoked() { + HttpCookieOAuth2AuthorizationRequestRepository cookieRequestRepo = new HttpCookieOAuth2AuthorizationRequestRepository(); + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + Cookie cookie = new Cookie(OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, SERIALIZED_ATTACK_STRING); + Mockito.when(request.getCookies()).thenReturn(new Cookie[]{cookie}); + + cookieRequestRepo.loadAuthorizationRequest(request); + + assertEquals(0, maliciousMethodInvocationCounter); + } + + private static class MaliciousClass implements Serializable { + private static final long serialVersionUID = 0L; + + public void maliciousMethod() { + maliciousMethodInvocationCounter++; + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + maliciousMethod(); + } + } +}