This is an automated email from the ASF dual-hosted git repository.

hanicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new c3dee5634 KNOX-3416: KnoxSSO redirects to untrusted site (#1346)
c3dee5634 is described below

commit c3dee56346cfb2670a114d478db0ba2b51aa01f7
Author: hanicz <[email protected]>
AuthorDate: Wed Aug 12 15:26:33 2026 +0200

    KNOX-3416: KnoxSSO redirects to untrusted site (#1346)
    
    * KNOX-3416: KnoxSSO redirects to untrusted site
    
    * KNOX-3416: Clarify error message for userInfo
---
 .../gateway/service/knoxsso/KnoxSSOMessages.java   |  4 ++
 .../gateway/service/knoxsso/WebSSOResource.java    | 25 ++++++++-----
 .../service/knoxsso/WebSSOResourceTest.java        | 43 ++++++++++++++++++++++
 3 files changed, 63 insertions(+), 9 deletions(-)

diff --git 
a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOMessages.java
 
b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOMessages.java
index 3e642219b..b9a87cfbe 100644
--- 
a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOMessages.java
+++ 
b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOMessages.java
@@ -61,6 +61,10 @@ public interface KnoxSSOMessages {
       "not valid according to the configured whitelist: {1}. See documentation 
for KnoxSSO Whitelisting.")
   void whiteListMatchFail(String original, String whitelist);
 
+  @Message( level = MessageLevel.ERROR, text = "The original URL: {0} for 
redirecting back after authentication " +
+      "embeds userinfo (e.g. user@host) and is therefore rejected.")
+  void userInfoInOriginalURL(String original);
+
   @Message( level = MessageLevel.INFO, text = "Knox Token service ({0}) stored 
state for token {1} ({2})")
   void storedToken(String topologyName, String tokenDisplayText, String 
tokenId);
 }
diff --git 
a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
 
b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
index 16e676240..cf73bc987 100644
--- 
a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
+++ 
b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
@@ -264,20 +264,27 @@ public class WebSSOResource {
 
       boolean validRedirect = true;
 
-      // If there is a whitelist defined, then the original URL must be 
validated against it.
-      // If there is no whitelist, then everything is valid.
-      if (whitelist != null) {
-        try {
+      try {
+        // A redirect target embedding userinfo (e.g. 
https://knox-host:8443@evil/) is
+        // never legitimate; reject it before the host-only whitelist check.
+        if (Urls.containsUserInfo(original)) {
+          validRedirect = false;
+          
LOGGER.userInfoInOriginalURL(Log4jAuditor.maskTokenFromURL(original));
+        } else if (whitelist != null) {
+          // If there is a whitelist defined, then the original URL must be 
validated against it.
+          // If there is no whitelist, then everything is valid.
           validRedirect = RegExUtils.checkBaseUrlAgainstWhitelist(whitelist, 
original);
-        } catch (MalformedURLException e) {
-          throw new WebApplicationException("Malformed original URL: " + 
original,
-                  Response.Status.BAD_REQUEST);
+          if (!validRedirect) {
+            LOGGER.whiteListMatchFail(Log4jAuditor.maskTokenFromURL(original), 
whitelist);
+          }
         }
+      } catch (MalformedURLException e) {
+        throw new WebApplicationException("Malformed original URL: " + 
original,
+                Response.Status.BAD_REQUEST);
       }
 
       if (!validRedirect) {
-        LOGGER.whiteListMatchFail(Log4jAuditor.maskTokenFromURL(original), 
whitelist);
-        throw new WebApplicationException("Original URL not valid according to 
the configured whitelist.",
+        throw new WebApplicationException("Original URL not valid for 
redirect.",
                                           Response.Status.BAD_REQUEST);
       }
     } else {
diff --git 
a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
 
b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
index f0e119478..12d84ab0c 100644
--- 
a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
+++ 
b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -473,6 +473,49 @@ public class WebSSOResourceTest {
     }
   }
 
+  @Test
+  public void testUserInfoOriginalURLRejected() throws Exception {
+    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+    
EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(expectGatewayConfig()).anyTimes();
+
+    HttpServletRequest request = 
EasyMock.createNiceMock(HttpServletRequest.class);
+    EasyMock.expect(request.getParameter("originalUrl")).andReturn(
+        "https://localhost:[email protected]/";);
+    
EasyMock.expect(request.getAttribute("targetServiceRole")).andReturn("KNOXSSO").anyTimes();
+    
EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
+    EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+    EasyMock.expect(request.getServerName()).andReturn("localhost").anyTimes();
+
+    Principal principal = EasyMock.createNiceMock(Principal.class);
+    EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+    
EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+    GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+    
EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+    AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+    
EasyMock.expect(services.getService(ServiceType.ALIAS_SERVICE)).andReturn(aliasService).anyTimes();
+    
EasyMock.expect(aliasService.getPasswordFromAliasForGateway(TokenUtils.SIGNING_HMAC_SECRET_ALIAS)).andReturn(null).anyTimes();
+
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, 
gatewayPrivateKey);
+    
EasyMock.expect(services.getService(ServiceType.TOKEN_SERVICE)).andReturn(authority);
+
+    HttpServletResponse response = 
EasyMock.createNiceMock(HttpServletResponse.class);
+    ServletOutputStream outputStream = 
EasyMock.createNiceMock(ServletOutputStream.class);
+    CookieResponseWrapper responseWrapper = new 
CookieResponseWrapper(response, outputStream);
+
+    EasyMock.replay(principal, services, context, request);
+
+    WebSSOResource webSSOResponse = new WebSSOResource();
+    webSSOResponse.request = request;
+    webSSOResponse.response = responseWrapper;
+    webSSOResponse.context = context;
+    webSSOResponse.init();
+
+    WebApplicationException e = 
Assert.assertThrows(WebApplicationException.class, webSSOResponse::doGet);
+    assertEquals(HttpStatus.SC_BAD_REQUEST, e.getResponse().getStatus());
+  }
+
   private GatewayConfig expectGatewayConfig() {
     return expectGatewayConfig(true);
   }

Reply via email to