Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jans-auth-server): added restriction for request_uri parameter (blocklist and allowed client.request_uri) #1503 #1556

Merged
merged 1 commit into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public class AppConfiguration implements Configuration {
private Boolean requestUriParameterSupported;
private Boolean requestUriHashVerificationEnabled;
private Boolean requireRequestUriRegistration;
private List<String> requestUriBlockList;
private String opPolicyUri;
private String opTosUri;
private int authorizationCodeLifetime;
Expand Down Expand Up @@ -1399,6 +1400,15 @@ public void setRequireRequestUriRegistration(Boolean requireRequestUriRegistrati
this.requireRequestUriRegistration = requireRequestUriRegistration;
}

public List<String> getRequestUriBlockList() {
if (requestUriBlockList == null) requestUriBlockList = Lists.newArrayList();
return requestUriBlockList;
}

public void setRequestUriBlockList(List<String> requestUriBlockList) {
this.requestUriBlockList = requestUriBlockList;
}

public String getOpPolicyUri() {
return opPolicyUri;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> 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());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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());
}
}
5 changes: 5 additions & 0 deletions jans-config-api/docs/jans-config-api-swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@
"claimsParameterSupported":true,
"requestParameterSupported":true,
"requestUriParameterSupported":true,
"requestUriBlockList": [
"localhost",
"127.0.0.1"
],
"requireRequestUriRegistration":false,
"allowPostLogoutRedirectWithoutValidation":false,
"introspectionAccessTokenMustHaveUmaProtectionScope":false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@
"claimsParameterSupported":false,
"requestParameterSupported":true,
"requestUriParameterSupported":true,
"requestUriBlockList": [
"localhost",
"127.0.0.1"
],
"requireRequestUriRegistration":false,
"allowPostLogoutRedirectWithoutValidation":false,
"introspectionAccessTokenMustHaveUmaProtectionScope":false,
Expand Down