Browse Source

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
pull/411/head
Anoop Garlapati 5 years ago committed by Joe Grandja
parent
commit
1d4dcddc11
  1. 27
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java

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

@ -18,6 +18,7 @@ package org.springframework.security.oauth2.server.authorization.authentication; @@ -18,6 +18,7 @@ package org.springframework.security.oauth2.server.authorization.authentication;
import java.security.Principal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
@ -26,7 +27,6 @@ import java.util.Map; @@ -26,7 +27,6 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
@ -35,6 +35,7 @@ import org.springframework.security.core.AuthenticationException; @@ -35,6 +35,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2TokenType;
@ -45,7 +46,6 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; @@ -45,7 +46,6 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@ -72,8 +72,6 @@ import org.springframework.web.util.UriComponentsBuilder; @@ -72,8 +72,6 @@ import org.springframework.web.util.UriComponentsBuilder;
public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implements AuthenticationProvider {
private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);
private static final String PKCE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1";
private static final Pattern LOOPBACK_ADDRESS_PATTERN =
Pattern.compile("^127(?:\\.[0-9]+){0,2}\\.[0-9]+$|^\\[(?:0*:)*?:?0*1]$");
private static final StringKeyGenerator DEFAULT_AUTHORIZATION_CODE_GENERATOR =
new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
private static final StringKeyGenerator DEFAULT_STATE_GENERATOR =
@ -417,7 +415,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen @@ -417,7 +415,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
// redirects described in Section 10.3.3, the use of "localhost" is NOT RECOMMENDED.
return false;
}
if (!LOOPBACK_ADDRESS_PATTERN.matcher(requestedRedirectHost).matches()) {
if (!isLoopbackAddress(requestedRedirectHost)) {
// As per https://tools.ietf.org/html/draft-ietf-oauth-v2-1-01#section-9.7
// When comparing client redirect URIs against pre-registered URIs,
// authorization servers MUST utilize exact string matching.
@ -439,6 +437,25 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen @@ -439,6 +437,25 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
return false;
}
private static boolean isLoopbackAddress(String host) {
// IPv6 loopback address should either be "0:0:0:0:0:0:0:1" or "::1"
if ("[0:0:0:0:0:0:0:1]".equals(host) || "[::1]".equals(host)) {
return true;
}
// IPv4 loopback address ranges from 127.0.0.1 to 127.255.255.255
String[] ipv4Octets = host.split("\\.");
if (ipv4Octets.length != 4) {
return false;
}
try {
int[] address = Arrays.stream(ipv4Octets).mapToInt(Integer::parseInt).toArray();
return address[0] == 127 && address[1] >= 0 && address[1] <= 255 && address[2] >= 0 &&
address[2] <= 255 && address[3] >= 1 && address[3] <= 255;
} catch (NumberFormatException ex) {
return false;
}
}
private static boolean isPrincipalAuthenticated(Authentication principal) {
return principal != null &&
!AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&

Loading…
Cancel
Save