Skip to content

Commit 1d4dcdd

Browse files
anoopgarlapatijgrandja
authored andcommitted
Polish loopback address validation in DefaultRedirectUriOAuth2AuthenticationValidator
Changed loopback address validation from regex to explicit validation using IPv4 loopback address range and IPv6 address. Issue gh-243
1 parent 3ee47ef commit 1d4dcdd

File tree

1 file changed

+22
-5
lines changed

1 file changed

+22
-5
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.security.Principal;
1919
import java.time.Instant;
2020
import java.time.temporal.ChronoUnit;
21+
import java.util.Arrays;
2122
import java.util.Base64;
2223
import java.util.Collections;
2324
import java.util.HashMap;
@@ -26,7 +27,6 @@
2627
import java.util.Set;
2728
import java.util.function.Function;
2829
import java.util.function.Supplier;
29-
import java.util.regex.Pattern;
3030

3131
import org.springframework.security.authentication.AnonymousAuthenticationToken;
3232
import org.springframework.security.authentication.AuthenticationProvider;
@@ -35,6 +35,7 @@
3535
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
3636
import org.springframework.security.crypto.keygen.StringKeyGenerator;
3737
import org.springframework.security.oauth2.core.AuthorizationGrantType;
38+
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
3839
import org.springframework.security.oauth2.core.OAuth2Error;
3940
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
4041
import org.springframework.security.oauth2.core.OAuth2TokenType;
@@ -45,7 +46,6 @@
4546
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
4647
import org.springframework.security.oauth2.core.oidc.OidcScopes;
4748
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
48-
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
4949
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
5050
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
5151
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@@ -72,8 +72,6 @@
7272
public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implements AuthenticationProvider {
7373
private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);
7474
private static final String PKCE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1";
75-
private static final Pattern LOOPBACK_ADDRESS_PATTERN =
76-
Pattern.compile("^127(?:\\.[0-9]+){0,2}\\.[0-9]+$|^\\[(?:0*:)*?:?0*1]$");
7775
private static final StringKeyGenerator DEFAULT_AUTHORIZATION_CODE_GENERATOR =
7876
new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
7977
private static final StringKeyGenerator DEFAULT_STATE_GENERATOR =
@@ -417,7 +415,7 @@ private static boolean isValidRedirectUri(String requestedRedirectUri, Registere
417415
// redirects described in Section 10.3.3, the use of "localhost" is NOT RECOMMENDED.
418416
return false;
419417
}
420-
if (!LOOPBACK_ADDRESS_PATTERN.matcher(requestedRedirectHost).matches()) {
418+
if (!isLoopbackAddress(requestedRedirectHost)) {
421419
// As per https://tools.ietf.org/html/draft-ietf-oauth-v2-1-01#section-9.7
422420
// When comparing client redirect URIs against pre-registered URIs,
423421
// authorization servers MUST utilize exact string matching.
@@ -439,6 +437,25 @@ private static boolean isValidRedirectUri(String requestedRedirectUri, Registere
439437
return false;
440438
}
441439

440+
private static boolean isLoopbackAddress(String host) {
441+
// IPv6 loopback address should either be "0:0:0:0:0:0:0:1" or "::1"
442+
if ("[0:0:0:0:0:0:0:1]".equals(host) || "[::1]".equals(host)) {
443+
return true;
444+
}
445+
// IPv4 loopback address ranges from 127.0.0.1 to 127.255.255.255
446+
String[] ipv4Octets = host.split("\\.");
447+
if (ipv4Octets.length != 4) {
448+
return false;
449+
}
450+
try {
451+
int[] address = Arrays.stream(ipv4Octets).mapToInt(Integer::parseInt).toArray();
452+
return address[0] == 127 && address[1] >= 0 && address[1] <= 255 && address[2] >= 0 &&
453+
address[2] <= 255 && address[3] >= 1 && address[3] <= 255;
454+
} catch (NumberFormatException ex) {
455+
return false;
456+
}
457+
}
458+
442459
private static boolean isPrincipalAuthenticated(Authentication principal) {
443460
return principal != null &&
444461
!AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&

0 commit comments

Comments
 (0)