From 47804ee8348bb9227350f65053a722a551c242b2 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Thu, 18 Jun 2026 16:48:43 -0600 Subject: [PATCH] Use MessageDigest#isEqual This commit favors constant-time comparison to mitigate timing attacks Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com> --- .../InMemoryOAuth2AuthorizationService.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java b/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java index c84d57f70a..617db00330 100644 --- a/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java +++ b/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java @@ -16,6 +16,8 @@ package org.springframework.security.oauth2.server.authorization; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; @@ -191,38 +193,43 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza } private static boolean matchesState(OAuth2Authorization authorization, String token) { - return token.equals(authorization.getAttribute(OAuth2ParameterNames.STATE)); + return isEqual(authorization.getAttribute(OAuth2ParameterNames.STATE), token); } private static boolean matchesAuthorizationCode(OAuth2Authorization authorization, String token) { OAuth2Authorization.Token authorizationCode = authorization .getToken(OAuth2AuthorizationCode.class); - return authorizationCode != null && authorizationCode.getToken().getTokenValue().equals(token); + return authorizationCode != null && isEqual(authorizationCode.getToken().getTokenValue(), token); } private static boolean matchesAccessToken(OAuth2Authorization authorization, String token) { OAuth2Authorization.Token accessToken = authorization.getToken(OAuth2AccessToken.class); - return accessToken != null && accessToken.getToken().getTokenValue().equals(token); + return accessToken != null && isEqual(accessToken.getToken().getTokenValue(), token); } private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) { OAuth2Authorization.Token refreshToken = authorization.getToken(OAuth2RefreshToken.class); - return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token); + return refreshToken != null && isEqual(refreshToken.getToken().getTokenValue(), token); } private static boolean matchesIdToken(OAuth2Authorization authorization, String token) { OAuth2Authorization.Token idToken = authorization.getToken(OidcIdToken.class); - return idToken != null && idToken.getToken().getTokenValue().equals(token); + return idToken != null && isEqual(idToken.getToken().getTokenValue(), token); } private static boolean matchesDeviceCode(OAuth2Authorization authorization, String token) { OAuth2Authorization.Token deviceCode = authorization.getToken(OAuth2DeviceCode.class); - return deviceCode != null && deviceCode.getToken().getTokenValue().equals(token); + return deviceCode != null && isEqual(deviceCode.getToken().getTokenValue(), token); } private static boolean matchesUserCode(OAuth2Authorization authorization, String token) { OAuth2Authorization.Token userCode = authorization.getToken(OAuth2UserCode.class); - return userCode != null && userCode.getToken().getTokenValue().equals(token); + return userCode != null && isEqual(userCode.getToken().getTokenValue(), token); + } + + private static boolean isEqual(@Nullable String left, @Nullable String right) { + return left != null && right != null + && MessageDigest.isEqual(left.getBytes(StandardCharsets.UTF_8), right.getBytes(StandardCharsets.UTF_8)); } @SuppressWarnings("serial")