From 0696d92094eeb2ed36f6b0075680634acbf8992f Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Wed, 15 Jun 2022 10:40:16 +0300 Subject: [PATCH] feat(jans-auth-server): added restriction for request_uri parameter (blocklist and allowed client.request_uri) #1503 https://github.com/JanssenProject/jans/issues/1503 --- .../model/configuration/AppConfiguration.java | 10 +++ .../authorize/JwtAuthorizationRequest.java | 37 +++++++++++ .../server/service/RedirectUriResponse.java | 4 ++ .../server/service/RedirectionUriService.java | 7 +- .../JwtAuthorizationRequestTest.java | 66 +++++++++++++++++++ .../docs/jans-config-api-swagger.yaml | 5 ++ .../templates/jans-auth/jans-auth-config.json | 4 ++ .../templates/jans-auth/jans-auth-config.json | 4 ++ 8 files changed, 134 insertions(+), 3 deletions(-) diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java index c1b2e236842..c48f4028d27 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java @@ -126,6 +126,7 @@ public class AppConfiguration implements Configuration { private Boolean requestUriParameterSupported; private Boolean requestUriHashVerificationEnabled; private Boolean requireRequestUriRegistration; + private List requestUriBlockList; private String opPolicyUri; private String opTosUri; private int authorizationCodeLifetime; @@ -1399,6 +1400,15 @@ public void setRequireRequestUriRegistration(Boolean requireRequestUriRegistrati this.requireRequestUriRegistration = requireRequestUriRegistration; } + public List getRequestUriBlockList() { + if (requestUriBlockList == null) requestUriBlockList = Lists.newArrayList(); + return requestUriBlockList; + } + + public void setRequestUriBlockList(List requestUriBlockList) { + this.requestUriBlockList = requestUriBlockList; + } + public String getOpPolicyUri() { return opPolicyUri; } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/authorize/JwtAuthorizationRequest.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/authorize/JwtAuthorizationRequest.java index aac864aae58..536237d544a 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/authorize/JwtAuthorizationRequest.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/authorize/JwtAuthorizationRequest.java @@ -20,6 +20,7 @@ import io.jans.as.model.crypto.encryption.KeyEncryptionAlgorithm; import io.jans.as.model.crypto.signature.AlgorithmFamily; import io.jans.as.model.crypto.signature.SignatureAlgorithm; +import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.exception.InvalidJwtException; import io.jans.as.model.jwe.Jwe; import io.jans.as.model.jwe.JweDecrypterImpl; @@ -28,11 +29,14 @@ import io.jans.as.model.jwt.JwtHeaderName; import io.jans.as.model.util.Base64Util; import io.jans.as.model.util.JwtUtil; +import io.jans.as.model.util.URLPatternList; import io.jans.as.model.util.Util; import io.jans.as.server.service.ClientService; import io.jans.as.server.service.RedirectUriResponse; +import io.jans.as.server.service.RedirectionUriService; import io.jans.service.cdi.util.CdiUtil; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONArray; @@ -509,6 +513,7 @@ private static String queryRequest(@Nullable String requestUri, @Nullable Redire } public static JwtAuthorizationRequest createJwtRequest(String request, String requestUri, Client client, RedirectUriResponse redirectUriResponse, AbstractCryptoProvider cryptoProvider, AppConfiguration appConfiguration) { + validateRequestUri(requestUri, client, appConfiguration, redirectUriResponse.getState()); final String requestFromClient = queryRequest(requestUri, redirectUriResponse, appConfiguration); if (StringUtils.isNotBlank(requestFromClient)) { request = requestFromClient; @@ -572,4 +577,36 @@ public static void validateNbf(Integer nbf) throws InvalidJwtException { throw new InvalidJwtException("nbf claim is more then 60 in the past"); } } + + public static void validateRequestUri(String requestUri, Client client, AppConfiguration appConfiguration, String state) { + validateRequestUri(requestUri, client, appConfiguration, state, CdiUtil.bean(ErrorResponseFactory.class)); + } + + public static void validateRequestUri(String requestUri, Client client, AppConfiguration appConfiguration, String state, ErrorResponseFactory errorResponseFactory) { + if (StringUtils.isBlank(requestUri)) { + return; // nothing to validate + } + + // client.requestUris() - validation + if (ArrayUtils.isNotEmpty(client.getRequestUris()) && !RedirectionUriService.isUriEqual(requestUri, client.getRequestUris())) { + log.debug("request_uri is forbidden by client request uris."); + throw new WebApplicationException(Response + .status(Response.Status.BAD_REQUEST) + .entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.INVALID_REQUEST_URI, state, "")) + .build()); + } + + // check block list + final List blockList = appConfiguration.getRequestUriBlockList(); + if (!blockList.isEmpty()) { + URLPatternList urlPatternList = new URLPatternList(blockList); + if (urlPatternList.isUrlListed(requestUri)) { + log.debug("request_uri is forbidden by requestUriBlackList configuration."); + throw new WebApplicationException(Response + .status(Response.Status.BAD_REQUEST) + .entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.INVALID_REQUEST_URI, state, "")) + .build()); + } + } + } } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectUriResponse.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectUriResponse.java index 18809830686..0b0aac8b631 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectUriResponse.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectUriResponse.java @@ -54,6 +54,10 @@ public void setState(String state) { this.state = state; } + public String getState() { + return state; + } + public Response.ResponseBuilder createErrorBuilder(IErrorType errorType) { redirectUri.parseQueryString(errorFactory.getErrorAsQueryString(errorType, state)); return RedirectUtil.getRedirectResponseBuilder(redirectUri, httpRequest); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java index 12a58330e53..a5e1ffae2fc 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/RedirectionUriService.java @@ -26,6 +26,8 @@ import jakarta.inject.Named; import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.core.Response; +import org.slf4j.LoggerFactory; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,8 +41,7 @@ @Named public class RedirectionUriService { - @Inject - private Logger log; + private static final Logger log = LoggerFactory.getLogger(RedirectionUriService.class); @Inject private ClientService clientService; @@ -138,7 +139,7 @@ public String validateRedirectionUri(@NotNull Client client, String redirectionU return null; } - public boolean isUriEqual(String redirectionUri, String[] redirectUris) { + public static boolean isUriEqual(String redirectionUri, String[] redirectUris) { final String redirectUriWithoutParams = uriWithoutParams(redirectionUri); for (String uri : redirectUris) { diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/model/authorize/JwtAuthorizationRequestTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/model/authorize/JwtAuthorizationRequestTest.java index cfce622eee5..57d22a95682 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/model/authorize/JwtAuthorizationRequestTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/model/authorize/JwtAuthorizationRequestTest.java @@ -2,9 +2,14 @@ import io.jans.as.common.model.registration.Client; import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.exception.InvalidJwtException; +import jakarta.ws.rs.WebApplicationException; import org.testng.annotations.Test; +import java.util.Arrays; +import java.util.Collections; + /** * @author Yuriy Zabrovarnyy */ @@ -18,4 +23,65 @@ public void createJwtAuthorizationRequest_whenEncryptionIsRequiredForUnencrypted String signedJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; new JwtAuthorizationRequest(appConfiguration, null, signedJwt, new Client()); } + + @Test + public void validateRequestUri_whichIsAllowedByClient_shouldBeOk() { + String requestUri = "https://myrp.com/request_uri"; + + Client client = new Client(); + client.setRequestUris(new String[]{"https://myrp.com/request_uri"}); + JwtAuthorizationRequest.validateRequestUri(requestUri, client, new AppConfiguration(), "", new ErrorResponseFactory()); + } + + @Test + public void validateRequestUri_withNoRestrictions_shouldBeOk() { + String requestUri = "https://myrp.com/request_uri"; + + JwtAuthorizationRequest.validateRequestUri(requestUri, new Client(), new AppConfiguration(), "", new ErrorResponseFactory()); + } + + @Test(expectedExceptions = WebApplicationException.class) + public void validateRequestUri_whichIsNotAllowedByClient_shouldRaiseException() { + String requestUri = "https://myrp.com/request_uri"; + + Client client = new Client(); + client.setRequestUris(new String[]{"https://myrp.com"}); + JwtAuthorizationRequest.validateRequestUri(requestUri, client, new AppConfiguration(), "", new ErrorResponseFactory()); + } + + @Test(expectedExceptions = WebApplicationException.class) + public void validateRequestUri_whichIsBlockListed_shouldRaiseException() { + String requestUri = "https://myrp.com/request_uri"; + + final AppConfiguration appConfiguration = new AppConfiguration(); + appConfiguration.setRequestUriBlockList(Arrays.asList("myrp.com", "evil.com")); + JwtAuthorizationRequest.validateRequestUri(requestUri, new Client(), appConfiguration, "", new ErrorResponseFactory()); + } + + @Test(expectedExceptions = WebApplicationException.class) + public void validateRequestUri_forLocalhost_shouldRaiseException() { + String requestUri = "https://localhost/request_uri"; + + final AppConfiguration appConfiguration = new AppConfiguration(); + appConfiguration.setRequestUriBlockList(Collections.singletonList("localhost")); + JwtAuthorizationRequest.validateRequestUri(requestUri, new Client(), appConfiguration, "", new ErrorResponseFactory()); + } + + @Test(expectedExceptions = WebApplicationException.class) + public void validateRequestUri_forLocalhostIp_shouldRaiseException() { + String requestUri = "https://127.0.0.1/request_uri"; + + final AppConfiguration appConfiguration = new AppConfiguration(); + appConfiguration.setRequestUriBlockList(Collections.singletonList("127.0.0.1")); + JwtAuthorizationRequest.validateRequestUri(requestUri, new Client(), appConfiguration, "", new ErrorResponseFactory()); + } + + @Test + public void validateRequestUri_whichIsNotBlockListed_shouldBeOk() { + String requestUri = "https://myrp.com/request_uri"; + + final AppConfiguration appConfiguration = new AppConfiguration(); + appConfiguration.setRequestUriBlockList(Arrays.asList("evil.com", "second.com")); + JwtAuthorizationRequest.validateRequestUri(requestUri, new Client(), appConfiguration, "", new ErrorResponseFactory()); + } } diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index d2ed0c4eeda..6458d1df366 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -4164,6 +4164,11 @@ components: requestUriParameterSupported: type: boolean description: Boolean value specifying whether the OP supports use of the request_uri parameter. + requestUriBlockList: + type: array + description: Block list for requestUri that can come to Authorization Endpoint (e.g. "localhost") + items: + type: string requestUriHashVerificationEnabled: type: boolean description: Boolean value specifying whether the OP supports use of the request_uri hash verification. diff --git a/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json b/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json index 49658bf698a..3e9feb8b93a 100644 --- a/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json +++ b/jans-linux-setup/jans_setup/openbanking/templates/jans-auth/jans-auth-config.json @@ -182,6 +182,10 @@ "claimsParameterSupported":true, "requestParameterSupported":true, "requestUriParameterSupported":true, + "requestUriBlockList": [ + "localhost", + "127.0.0.1" + ], "requireRequestUriRegistration":false, "allowPostLogoutRedirectWithoutValidation":false, "introspectionAccessTokenMustHaveUmaProtectionScope":false, diff --git a/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json b/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json index 3b107056266..b49fddcafd8 100644 --- a/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json +++ b/jans-linux-setup/jans_setup/templates/jans-auth/jans-auth-config.json @@ -261,6 +261,10 @@ "claimsParameterSupported":false, "requestParameterSupported":true, "requestUriParameterSupported":true, + "requestUriBlockList": [ + "localhost", + "127.0.0.1" + ], "requireRequestUriRegistration":false, "allowPostLogoutRedirectWithoutValidation":false, "introspectionAccessTokenMustHaveUmaProtectionScope":false,