CVE-2018-11041
Description
Cloud Foundry UAA, versions later than 4.6.0 and prior to 4.19.0 except 4.10.1 and 4.7.5 and uaa-release versions later than v48 and prior to v60 except v55.1 and v52.9, does not validate redirect URL values on a form parameter used for internal UAA redirects on the login page, allowing open redirects. A remote attacker can craft a malicious link that, when clicked, will redirect users to arbitrary websites after a successful login attempt.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Cloud Foundry UAA fails to validate the redirect URL on the login page, allowing an open redirect after a successful login.
Vulnerability
Cloud Foundry UAA version later than 4.6.0 and prior to 4.19.0 (except 4.10.1 and 4.7.5) and uaa-release versions later than v48 and prior to v60 (except v55.1 and v52.9) do not validate the form_redirect_uri parameter on the login page, enabling an open redirect vulnerability [2]. The UAA server performs internal redirects after authentication, but fails to check if the target URL belongs to an allowed domain [1].
Exploitation
A remote attacker crafts a malicious link that points to a legitimate UAA login page but includes a crafted form_redirect_uri parameter targeting an external site [2]. When a victim clicks this link and completes a successful login, the server redirects the victim’s browser to the attacker-controlled external URL. No additional authentication or privileges are required beyond the ability to host a link and the victim’s willing click [2].
Impact
A successful open redirect allows an attacker to direct users to arbitrary external websites after authentication. This can be leveraged for phishing attacks, where users believe they are still within the Cloud Foundry environment, potentially leading to credential theft or malware installation. The redirect occurs in the user’s browser after a legitimate login, giving the attack a veneer of authenticity [2].
Mitigation
The vulnerability is fixed in UAA versions 4.10.1, 4.7.5, and 4.19.0, and in uaa-release versions v55.1 and v60 [2]. The fix adds validation of the redirect URL against the server’s expected hostname before saving the request [3][4]. Operators should upgrade to the latest patched version or apply the fix from the referenced commits. No workaround is available [2]. Not listed in CISA KEV.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | < 4.7.5 | 4.7.5 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 4.8.0, < 4.10.1 | 4.10.1 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 4.11.0, < 4.12.3 | 4.12.3 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 4.13.0, < 4.19.0 | 4.19.0 |
Affected products
2- Cloud Foundry/Cloud Foundry UAAv5Range: later than 4.6.0 and prior to 4.19.0 except 4.10.1 and 4.7.5
Patches
8d17b23fc3bf9Request cache only saves requests with form_redirect_uri if it's valid
5 files changed · +61 −19
server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtils.java+13 −0 modified@@ -219,4 +219,17 @@ public static String getRequestPath(HttpServletRequest request) { String path = String.format("%s%s", servletPath, pathInfo); return path; } + + public static boolean uriHasMatchingHost(String uri, String hostname) { + if (uri == null) { + return false; + } + + try { + URL url = new URL(uri); + return hostname.equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+2 −16 modified@@ -17,12 +17,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.net.MalformedURLException; -import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -40,23 +39,10 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { + } else if (UaaUrlUtils.uriHasMatchingHost(redirectFormParam, request.getServerName())) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } - - private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { - if (redirectUri == null) { - return false; - } - - try { - URL url = new URL(redirectUri); - return request.getServerName().equals(url.getHost()); - } catch (MalformedURLException e) { - return false; - } - } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java+3 −1 modified@@ -92,7 +92,9 @@ protected boolean shouldSaveFormRedirectParameter(HttpServletRequest request) { if (StringUtils.isEmpty(formRedirect)) { return false; } - + if (!UaaUrlUtils.uriHasMatchingHost(formRedirect, request.getServerName())) { + return false; + } if (hasSavedRequest(request)) { return false; }
server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtilsTest.java+15 −0 modified@@ -32,8 +32,10 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class UaaUrlUtilsTest { @@ -360,6 +362,19 @@ public void test_validate_invalid_redirect_uri() { validateRedirectUri(convertToHttps(invalidHttpWildCardUrls), false); } + @Test + public void testUriHasMatchingHost() { + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://subdomain.test.com/test", "subdomain.test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "1.2.3.4")); + + assertFalse(UaaUrlUtils.uriHasMatchingHost(null, "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://not-test.com/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("not-valid-url", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "1.2.3.4")); + } + private void validateRedirectUri(List<String> urls, boolean result) { Map<String, String> failed = getFailedUrls(urls, result); if (!failed.isEmpty()) {
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java+28 −2 modified@@ -28,8 +28,13 @@ import org.springframework.security.web.savedrequest.SavedRequest; import javax.servlet.FilterChain; +import javax.servlet.ServletException; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.FORM_REDIRECT_PARAMETER; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; @@ -38,6 +43,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -103,6 +109,7 @@ public void filter_saves_when_needed() throws Exception { request.setPathInfo("/login.do"); request.setRequestURI("/login.do"); request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); ServletResponse response = new MockHttpServletResponse(); @@ -145,8 +152,11 @@ public void saveFormRedirectRequest_GET_Method() throws Exception { @Test public void saveFormRedirectRequest() throws Exception { + String redirectUri = "http://login"; request.setSession(session); - request.setParameter(FORM_REDIRECT_PARAMETER, "http://login"); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); + spy.saveRequest(request, new MockHttpServletResponse()); verify(spy).saveClientRedirect(request, request.getParameter(FORM_REDIRECT_PARAMETER)); } @@ -169,14 +179,19 @@ public void only_save_for_POST_calls() { } @Test - public void should_save_condition_works() { + public void should_save_condition_works() throws MalformedURLException { assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setPathInfo("/login.do"); assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + request.setSession(session); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + ClientRedirectSavedRequest savedRequest = new ClientRedirectSavedRequest(request, redirectUri); session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); assertFalse(cache.shouldSaveFormRedirectParameter(request)); @@ -215,5 +230,16 @@ public void saved_request_matcher() { } + @Test + public void unapprovedFormRedirectRequestDoesNotSave() throws IOException, ServletException { + request.setPathInfo("/login.do"); + request.setRequestURI("/login.do"); + request.setMethod(HttpMethod.POST.name()); + request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); + request.setServerName("not-test.com"); + + spy.doFilter(request, new MockHttpServletResponse(), mock(FilterChain.class)); + verify(spy, never()).saveClientRedirect(any(HttpServletRequest.class), anyString()); + } } \ No newline at end of file
57a15dfb7e0eValidate form_redirect_uri parameter against the request host.
2 files changed · +33 −8
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+16 −1 modified@@ -21,6 +21,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.net.MalformedURLException; +import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -38,10 +40,23 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (redirectFormParam != null) { + } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } + + private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { + if (redirectUri == null) { + return false; + } + + try { + URL url = new URL(redirectUri); + return request.getServerName().equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandlerTests.java+17 −7 modified@@ -41,16 +41,26 @@ public void allow_url_override() { assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } - @Test - public void form_parameter_works() { - request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); - assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); - } - @Test public void form_parameter_is_overridden() { request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); request.setAttribute(URI_OVERRIDE_ATTRIBUTE, "http://override.test.com"); assertEquals("http://override.test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } -} \ No newline at end of file + + @Test + public void validFormRedirectIsReturned() { + String redirectUri = request.getScheme() + "://" + request.getServerName() + "/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals(redirectUri, handler.determineTargetUrl(request, new MockHttpServletResponse())); + } + + @Test + public void invalidFormRedirectIsNotReturned() { + String redirectUri = "http://test.com/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals("/", handler.determineTargetUrl(request, new MockHttpServletResponse())); + } +}
7d750e036cd5Validate form_redirect_uri parameter against the request host.
2 files changed · +33 −8
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+16 −1 modified@@ -21,6 +21,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.net.MalformedURLException; +import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -38,10 +40,23 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (redirectFormParam != null) { + } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } + + private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { + if (redirectUri == null) { + return false; + } + + try { + URL url = new URL(redirectUri); + return request.getServerName().equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandlerTests.java+17 −7 modified@@ -41,16 +41,26 @@ public void allow_url_override() { assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } - @Test - public void form_parameter_works() { - request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); - assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); - } - @Test public void form_parameter_is_overridden() { request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); request.setAttribute(URI_OVERRIDE_ATTRIBUTE, "http://override.test.com"); assertEquals("http://override.test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } -} \ No newline at end of file + + @Test + public void validFormRedirectIsReturned() { + String redirectUri = request.getScheme() + "://" + request.getServerName() + "/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals(redirectUri, handler.determineTargetUrl(request, new MockHttpServletResponse())); + } + + @Test + public void invalidFormRedirectIsNotReturned() { + String redirectUri = "http://test.com/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals("/", handler.determineTargetUrl(request, new MockHttpServletResponse())); + } +}
8a599448781aRequest cache only saves requests with form_redirect_uri if it's valid
5 files changed · +77 −37
server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtils.java+22 −10 modified@@ -14,6 +14,15 @@ */ package org.cloudfoundry.identity.uaa.util; +import org.cloudfoundry.identity.uaa.zone.IdentityZone; +import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; + import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; @@ -25,16 +34,6 @@ import java.util.Map; import java.util.regex.Pattern; -import org.cloudfoundry.identity.uaa.zone.IdentityZone; -import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; - -import org.springframework.util.AntPathMatcher; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.UriUtils; - import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import static org.springframework.util.StringUtils.hasText; @@ -228,4 +227,17 @@ public static String getRequestPath(HttpServletRequest request) { String path = String.format("%s%s", servletPath, pathInfo); return path; } + + public static boolean uriHasMatchingHost(String uri, String hostname) { + if (uri == null) { + return false; + } + + try { + URL url = new URL(uri); + return hostname.equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+2 −16 modified@@ -17,12 +17,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.net.MalformedURLException; -import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -40,23 +39,10 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { + } else if (UaaUrlUtils.uriHasMatchingHost(redirectFormParam, request.getServerName())) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } - - private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { - if (redirectUri == null) { - return false; - } - - try { - URL url = new URL(redirectUri); - return request.getServerName().equals(url.getHost()); - } catch (MalformedURLException e) { - return false; - } - } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java+3 −1 modified@@ -92,7 +92,9 @@ protected boolean shouldSaveFormRedirectParameter(HttpServletRequest request) { if (StringUtils.isEmpty(formRedirect)) { return false; } - + if (!UaaUrlUtils.uriHasMatchingHost(formRedirect, request.getServerName())) { + return false; + } if (hasSavedRequest(request)) { return false; }
server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtilsTest.java+22 −8 modified@@ -12,29 +12,30 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.util; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class UaaUrlUtilsTest { @@ -422,6 +423,19 @@ public void addSubdomain_handlesUnexpectedDotInSubdomain_currentZone() { assertEquals("http://somezone2.localhost:8080", url2); } + @Test + public void testUriHasMatchingHost() { + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://subdomain.test.com/test", "subdomain.test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "1.2.3.4")); + + assertFalse(UaaUrlUtils.uriHasMatchingHost(null, "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://not-test.com/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("not-valid-url", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "1.2.3.4")); + } + private void validateRedirectUri(List<String> urls, boolean result) { Map<String, String> failed = getFailedUrls(urls, result); if (!failed.isEmpty()) {
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java+28 −2 modified@@ -28,8 +28,13 @@ import org.springframework.security.web.savedrequest.SavedRequest; import javax.servlet.FilterChain; +import javax.servlet.ServletException; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.FORM_REDIRECT_PARAMETER; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; @@ -38,6 +43,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -103,6 +109,7 @@ public void filter_saves_when_needed() throws Exception { request.setPathInfo("/login.do"); request.setRequestURI("/login.do"); request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); ServletResponse response = new MockHttpServletResponse(); @@ -145,8 +152,11 @@ public void saveFormRedirectRequest_GET_Method() throws Exception { @Test public void saveFormRedirectRequest() throws Exception { + String redirectUri = "http://login"; request.setSession(session); - request.setParameter(FORM_REDIRECT_PARAMETER, "http://login"); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); + spy.saveRequest(request, new MockHttpServletResponse()); verify(spy).saveClientRedirect(request, request.getParameter(FORM_REDIRECT_PARAMETER)); } @@ -169,14 +179,19 @@ public void only_save_for_POST_calls() { } @Test - public void should_save_condition_works() { + public void should_save_condition_works() throws MalformedURLException { assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setPathInfo("/login.do"); assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + request.setSession(session); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + ClientRedirectSavedRequest savedRequest = new ClientRedirectSavedRequest(request, redirectUri); session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); assertFalse(cache.shouldSaveFormRedirectParameter(request)); @@ -215,5 +230,16 @@ public void saved_request_matcher() { } + @Test + public void unapprovedFormRedirectRequestDoesNotSave() throws IOException, ServletException { + request.setPathInfo("/login.do"); + request.setRequestURI("/login.do"); + request.setMethod(HttpMethod.POST.name()); + request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); + request.setServerName("not-test.com"); + + spy.doFilter(request, new MockHttpServletResponse(), mock(FilterChain.class)); + verify(spy, never()).saveClientRedirect(any(HttpServletRequest.class), anyString()); + } } \ No newline at end of file
238ce572fdaeRequest cache only saves requests with form_redirect_uri if it's valid
5 files changed · +77 −37
server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtils.java+22 −10 modified@@ -14,6 +14,15 @@ */ package org.cloudfoundry.identity.uaa.util; +import org.cloudfoundry.identity.uaa.zone.IdentityZone; +import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; + import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; @@ -25,16 +34,6 @@ import java.util.Map; import java.util.regex.Pattern; -import org.cloudfoundry.identity.uaa.zone.IdentityZone; -import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; - -import org.springframework.util.AntPathMatcher; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.UriUtils; - import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import static org.springframework.util.StringUtils.hasText; @@ -228,4 +227,17 @@ public static String getRequestPath(HttpServletRequest request) { String path = String.format("%s%s", servletPath, pathInfo); return path; } + + public static boolean uriHasMatchingHost(String uri, String hostname) { + if (uri == null) { + return false; + } + + try { + URL url = new URL(uri); + return hostname.equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+2 −16 modified@@ -17,12 +17,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.net.MalformedURLException; -import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -40,23 +39,10 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { + } else if (UaaUrlUtils.uriHasMatchingHost(redirectFormParam, request.getServerName())) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } - - private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { - if (redirectUri == null) { - return false; - } - - try { - URL url = new URL(redirectUri); - return request.getServerName().equals(url.getHost()); - } catch (MalformedURLException e) { - return false; - } - } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java+3 −1 modified@@ -92,7 +92,9 @@ protected boolean shouldSaveFormRedirectParameter(HttpServletRequest request) { if (StringUtils.isEmpty(formRedirect)) { return false; } - + if (!UaaUrlUtils.uriHasMatchingHost(formRedirect, request.getServerName())) { + return false; + } if (hasSavedRequest(request)) { return false; }
server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtilsTest.java+22 −8 modified@@ -12,29 +12,30 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.util; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class UaaUrlUtilsTest { @@ -422,6 +423,19 @@ public void addSubdomain_handlesUnexpectedDotInSubdomain_currentZone() { assertEquals("http://somezone2.localhost:8080", url2); } + @Test + public void testUriHasMatchingHost() { + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://subdomain.test.com/test", "subdomain.test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "1.2.3.4")); + + assertFalse(UaaUrlUtils.uriHasMatchingHost(null, "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://not-test.com/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("not-valid-url", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "1.2.3.4")); + } + private void validateRedirectUri(List<String> urls, boolean result) { Map<String, String> failed = getFailedUrls(urls, result); if (!failed.isEmpty()) {
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java+28 −2 modified@@ -28,8 +28,13 @@ import org.springframework.security.web.savedrequest.SavedRequest; import javax.servlet.FilterChain; +import javax.servlet.ServletException; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.FORM_REDIRECT_PARAMETER; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; @@ -38,6 +43,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -103,6 +109,7 @@ public void filter_saves_when_needed() throws Exception { request.setPathInfo("/login.do"); request.setRequestURI("/login.do"); request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); ServletResponse response = new MockHttpServletResponse(); @@ -145,8 +152,11 @@ public void saveFormRedirectRequest_GET_Method() throws Exception { @Test public void saveFormRedirectRequest() throws Exception { + String redirectUri = "http://login"; request.setSession(session); - request.setParameter(FORM_REDIRECT_PARAMETER, "http://login"); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); + spy.saveRequest(request, new MockHttpServletResponse()); verify(spy).saveClientRedirect(request, request.getParameter(FORM_REDIRECT_PARAMETER)); } @@ -169,14 +179,19 @@ public void only_save_for_POST_calls() { } @Test - public void should_save_condition_works() { + public void should_save_condition_works() throws MalformedURLException { assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setPathInfo("/login.do"); assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + request.setSession(session); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + ClientRedirectSavedRequest savedRequest = new ClientRedirectSavedRequest(request, redirectUri); session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); assertFalse(cache.shouldSaveFormRedirectParameter(request)); @@ -215,5 +230,16 @@ public void saved_request_matcher() { } + @Test + public void unapprovedFormRedirectRequestDoesNotSave() throws IOException, ServletException { + request.setPathInfo("/login.do"); + request.setRequestURI("/login.do"); + request.setMethod(HttpMethod.POST.name()); + request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); + request.setServerName("not-test.com"); + + spy.doFilter(request, new MockHttpServletResponse(), mock(FilterChain.class)); + verify(spy, never()).saveClientRedirect(any(HttpServletRequest.class), anyString()); + } } \ No newline at end of file
f6362a8f1865Request cache only saves requests with form_redirect_uri if it's valid
5 files changed · +77 −37
server/src/main/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtils.java+22 −10 modified@@ -14,6 +14,15 @@ */ package org.cloudfoundry.identity.uaa.util; +import org.cloudfoundry.identity.uaa.zone.IdentityZone; +import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; + import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; @@ -25,16 +34,6 @@ import java.util.Map; import java.util.regex.Pattern; -import org.cloudfoundry.identity.uaa.zone.IdentityZone; -import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; - -import org.springframework.util.AntPathMatcher; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.UriUtils; - import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import static org.springframework.util.StringUtils.hasText; @@ -228,4 +227,17 @@ public static String getRequestPath(HttpServletRequest request) { String path = String.format("%s%s", servletPath, pathInfo); return path; } + + public static boolean uriHasMatchingHost(String uri, String hostname) { + if (uri == null) { + return false; + } + + try { + URL url = new URL(uri); + return hostname.equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+2 −16 modified@@ -17,12 +17,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.cloudfoundry.identity.uaa.util.UaaUrlUtils; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.net.MalformedURLException; -import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -40,23 +39,10 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { + } else if (UaaUrlUtils.uriHasMatchingHost(redirectFormParam, request.getServerName())) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } - - private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { - if (redirectUri == null) { - return false; - } - - try { - URL url = new URL(redirectUri); - return request.getServerName().equals(url.getHost()); - } catch (MalformedURLException e) { - return false; - } - } }
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCache.java+3 −1 modified@@ -92,7 +92,9 @@ protected boolean shouldSaveFormRedirectParameter(HttpServletRequest request) { if (StringUtils.isEmpty(formRedirect)) { return false; } - + if (!UaaUrlUtils.uriHasMatchingHost(formRedirect, request.getServerName())) { + return false; + } if (hasSavedRequest(request)) { return false; }
server/src/test/java/org/cloudfoundry/identity/uaa/util/UaaUrlUtilsTest.java+22 −8 modified@@ -12,29 +12,30 @@ *******************************************************************************/ package org.cloudfoundry.identity.uaa.util; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class UaaUrlUtilsTest { @@ -422,6 +423,19 @@ public void addSubdomain_handlesUnexpectedDotInSubdomain_currentZone() { assertEquals("http://somezone2.localhost:8080", url2); } + @Test + public void testUriHasMatchingHost() { + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://subdomain.test.com/test", "subdomain.test.com")); + assertTrue(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "1.2.3.4")); + + assertFalse(UaaUrlUtils.uriHasMatchingHost(null, "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://not-test.com/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("not-valid-url", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://1.2.3.4/test", "test.com")); + assertFalse(UaaUrlUtils.uriHasMatchingHost("http://test.com/test", "1.2.3.4")); + } + private void validateRedirectUri(List<String> urls, boolean result) { Map<String, String> failed = getFailedUrls(urls, result); if (!failed.isEmpty()) {
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestCacheTests.java+28 −2 modified@@ -28,8 +28,13 @@ import org.springframework.security.web.savedrequest.SavedRequest; import javax.servlet.FilterChain; +import javax.servlet.ServletException; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.FORM_REDIRECT_PARAMETER; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; @@ -38,6 +43,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -103,6 +109,7 @@ public void filter_saves_when_needed() throws Exception { request.setPathInfo("/login.do"); request.setRequestURI("/login.do"); request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); ServletResponse response = new MockHttpServletResponse(); @@ -145,8 +152,11 @@ public void saveFormRedirectRequest_GET_Method() throws Exception { @Test public void saveFormRedirectRequest() throws Exception { + String redirectUri = "http://login"; request.setSession(session); - request.setParameter(FORM_REDIRECT_PARAMETER, "http://login"); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); + spy.saveRequest(request, new MockHttpServletResponse()); verify(spy).saveClientRedirect(request, request.getParameter(FORM_REDIRECT_PARAMETER)); } @@ -169,14 +179,19 @@ public void only_save_for_POST_calls() { } @Test - public void should_save_condition_works() { + public void should_save_condition_works() throws MalformedURLException { assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setPathInfo("/login.do"); assertFalse(cache.shouldSaveFormRedirectParameter(request)); + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + request.setServerName(new URL(redirectUri).getHost()); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + request.setSession(session); assertTrue(cache.shouldSaveFormRedirectParameter(request)); + ClientRedirectSavedRequest savedRequest = new ClientRedirectSavedRequest(request, redirectUri); session.setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, savedRequest); assertFalse(cache.shouldSaveFormRedirectParameter(request)); @@ -215,5 +230,16 @@ public void saved_request_matcher() { } + @Test + public void unapprovedFormRedirectRequestDoesNotSave() throws IOException, ServletException { + request.setPathInfo("/login.do"); + request.setRequestURI("/login.do"); + request.setMethod(HttpMethod.POST.name()); + request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); + request.setServerName("not-test.com"); + + spy.doFilter(request, new MockHttpServletResponse(), mock(FilterChain.class)); + verify(spy, never()).saveClientRedirect(any(HttpServletRequest.class), anyString()); + } } \ No newline at end of file
7a8f157f7e2fValidate form_redirect_uri parameter against the request host.
2 files changed · +33 −8
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+16 −1 modified@@ -21,6 +21,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.net.MalformedURLException; +import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -38,10 +40,23 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (redirectFormParam != null) { + } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } + + private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { + if (redirectUri == null) { + return false; + } + + try { + URL url = new URL(redirectUri); + return request.getServerName().equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandlerTests.java+17 −7 modified@@ -41,16 +41,26 @@ public void allow_url_override() { assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } - @Test - public void form_parameter_works() { - request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); - assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); - } - @Test public void form_parameter_is_overridden() { request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); request.setAttribute(URI_OVERRIDE_ATTRIBUTE, "http://override.test.com"); assertEquals("http://override.test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } -} \ No newline at end of file + + @Test + public void validFormRedirectIsReturned() { + String redirectUri = request.getScheme() + "://" + request.getServerName() + "/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals(redirectUri, handler.determineTargetUrl(request, new MockHttpServletResponse())); + } + + @Test + public void invalidFormRedirectIsNotReturned() { + String redirectUri = "http://test.com/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals("/", handler.determineTargetUrl(request, new MockHttpServletResponse())); + } +}
83c8627c2da7Validate form_redirect_uri parameter against the request host.
2 files changed · +33 −8
server/src/main/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandler.java+16 −1 modified@@ -21,6 +21,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.net.MalformedURLException; +import java.net.URL; public class UaaSavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public static final String SAVED_REQUEST_SESSION_ATTRIBUTE = "SPRING_SECURITY_SAVED_REQUEST"; @@ -38,10 +40,23 @@ public String determineTargetUrl(HttpServletRequest request, HttpServletResponse if (redirectAttribute !=null) { logger.debug("Returning redirectAttribute saved URI:"+redirectAttribute); return (String) redirectAttribute; - } else if (redirectFormParam != null) { + } else if (isApprovedFormRedirectUri(request, redirectFormParam)) { return redirectFormParam; } else { return super.determineTargetUrl(request, response); } } + + private boolean isApprovedFormRedirectUri(HttpServletRequest request, String redirectUri) { + if (redirectUri == null) { + return false; + } + + try { + URL url = new URL(redirectUri); + return request.getServerName().equals(url.getHost()); + } catch (MalformedURLException e) { + return false; + } + } }
server/src/test/java/org/cloudfoundry/identity/uaa/web/UaaSavedRequestAwareAuthenticationSuccessHandlerTests.java+17 −7 modified@@ -41,16 +41,26 @@ public void allow_url_override() { assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } - @Test - public void form_parameter_works() { - request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); - assertEquals("http://test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); - } - @Test public void form_parameter_is_overridden() { request.setParameter(FORM_REDIRECT_PARAMETER, "http://test.com"); request.setAttribute(URI_OVERRIDE_ATTRIBUTE, "http://override.test.com"); assertEquals("http://override.test.com", handler.determineTargetUrl(request, new MockHttpServletResponse())); } -} \ No newline at end of file + + @Test + public void validFormRedirectIsReturned() { + String redirectUri = request.getScheme() + "://" + request.getServerName() + "/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals(redirectUri, handler.determineTargetUrl(request, new MockHttpServletResponse())); + } + + @Test + public void invalidFormRedirectIsNotReturned() { + String redirectUri = "http://test.com/test"; + + request.setParameter(FORM_REDIRECT_PARAMETER, redirectUri); + assertEquals("/", handler.determineTargetUrl(request, new MockHttpServletResponse())); + } +}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- github.com/advisories/GHSA-xh4m-99qp-w483ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-11041ghsaADVISORY
- github.com/cloudfoundry/uaa/commit/238ce572fdaebbb8357b265d2f77eb9761199a09ghsaWEB
- github.com/cloudfoundry/uaa/commit/57a15dfb7e0e3a59019ebe951793b586512b196ghsaWEB
- github.com/cloudfoundry/uaa/commit/7a8f157f7e2feed2d0ebb63b163ff735b6340b9ghsaWEB
- github.com/cloudfoundry/uaa/commit/7d750e036cd52c5d30e73e28cbcae23126d7154ghsaWEB
- github.com/cloudfoundry/uaa/commit/83c8627c2da7845043b65e6ba354a64b4f9c6e2fghsaWEB
- github.com/cloudfoundry/uaa/commit/8a599448781acd481aa9dab1b0bde3424e00cedghsaWEB
- github.com/cloudfoundry/uaa/commit/d17b23fc3bf9b86f111774925afadfced75315cghsaWEB
- github.com/cloudfoundry/uaa/commit/f6362a8f1865314aa507fc5de772848b7e55236ghsaWEB
- www.cloudfoundry.org/blog/cve-2018-11041ghsaWEB
- www.cloudfoundry.org/blog/cve-2018-11041/mitrex_refsource_CONFIRM
News mentions
0No linked articles in our index yet.