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

reschke pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 386223e315 OAK-12259: oak-http: OakServlet mis-parses HTTP Basic 
credentials
386223e315 is described below

commit 386223e315e968de6233721cad0f7855c7a853ab
Author: Julian Reschke <[email protected]>
AuthorDate: Wed Jul 1 06:22:09 2026 +0200

    OAK-12259: oak-http: OakServlet mis-parses HTTP Basic credentials
---
 .../jackrabbit/oak/http/AuthorizationField.java    | 34 ++++++++---
 .../oak/http/AuthorizationFieldTest.java           | 68 ++++++++++++++++++++--
 2 files changed, 89 insertions(+), 13 deletions(-)

diff --git 
a/oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java 
b/oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java
index c572e4a3ed..13eda1a1d9 100644
--- 
a/oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java
+++ 
b/oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java
@@ -16,11 +16,12 @@
  */
 package org.apache.jackrabbit.oak.http;
 
-import org.apache.jackrabbit.util.Base64;
-
 import javax.jcr.SimpleCredentials;
 import javax.security.auth.login.LoginException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 import java.util.Enumeration;
+import java.util.Locale;
 import java.util.NoSuchElementException;
 
 public class AuthorizationField {
@@ -44,11 +45,30 @@ public class AuthorizationField {
         return parseCredentials(field);
     }
 
-    private static SimpleCredentials parseCredentials(String fieldValue) 
throws LoginException {
-        if (fieldValue.startsWith("Basic ")) {
-            String[] basic =
-                    Base64.decode(fieldValue.substring("Basic 
".length())).split(":");
-            return new SimpleCredentials(basic[0], basic[1].toCharArray());
+    private static SimpleCredentials parseCredentials(String rawFieldValue) 
throws LoginException {
+        boolean hasControls = rawFieldValue.chars().anyMatch(c -> c < ' ');
+        if (hasControls) {
+            throw new LoginException("Control characters are not allowed");
+        }
+
+        String fieldValue = rawFieldValue.trim().replaceAll(" +", " ");
+
+        if (fieldValue.toLowerCase(Locale.ENGLISH).startsWith("basic ")) {
+            String token68 = fieldValue.substring("basic ".length());
+            try {
+                String decoded = new 
String(Base64.getDecoder().decode(token68), StandardCharsets.UTF_8);
+                int colon = decoded.indexOf(':');
+                if (colon < 0) {
+                    throw new LoginException(
+                            "Malformed Basic credentials: missing ':' 
separator");
+                }
+                String userId = decoded.substring(0, colon);
+                String password = decoded.substring(colon + 1);
+
+                return new SimpleCredentials(userId, password.toCharArray());
+            } catch (IllegalArgumentException ex) {
+                throw new LoginException(ex.getMessage());
+            }
         } else {
             throw new LoginException("Only Basic Authentication supported");
         }
diff --git 
a/oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java
 
b/oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java
index e2a3ab214d..4579db6c56 100644
--- 
a/oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java
+++ 
b/oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java
@@ -47,12 +47,10 @@ public class AuthorizationFieldTest {
         assertEquals("bar", new String(credentials.getPassword()));
     }
 
-    @Test(expected = ArrayIndexOutOfBoundsException.class) // BUG: OAK-12259
+    @Test(expected = LoginException.class)
     public void testInvalidBase64() throws LoginException {
         String b64 = 
Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
-        SimpleCredentials credentials = 
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic dksjdkj" + 
b64)));
-        assertEquals("foo", credentials.getUserID());
-        assertEquals("bar", new String(credentials.getPassword()));
+        AuthorizationField.valueOf(Collections.enumeration(List.of("Basic 
dksjdkj" + b64)));
     }
 
     @Test(expected = LoginException.class)
@@ -64,9 +62,67 @@ public class AuthorizationFieldTest {
     @Test
     public void testColonInPassword() throws LoginException {
         String b64 = "Basic " + 
Base64.getEncoder().encodeToString("foo:bar:qux".getBytes(StandardCharsets.UTF_8));
-        SimpleCredentials credentials 
=AuthorizationField.valueOf(Collections.enumeration(List.of(b64)));
+        SimpleCredentials credentials = 
AuthorizationField.valueOf(Collections.enumeration(List.of(b64)));
+        assertEquals("foo", credentials.getUserID());
+        assertEquals("bar:qux", new String(credentials.getPassword()));
+    }
+
+    @Test(expected = LoginException.class)
+    public void testMissingColon() throws LoginException {
+        String b64 = "Basic " + 
Base64.getEncoder().encodeToString("foobarqux".getBytes(StandardCharsets.UTF_8));
+        AuthorizationField.valueOf(Collections.enumeration(List.of(b64)));
+    }
+
+    @Test
+    public void testSchemeCase() throws LoginException {
+        String b64 = 
Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
+        SimpleCredentials credentials = 
AuthorizationField.valueOf(Collections.enumeration(List.of("BaSiC " + b64)));
+        assertEquals("foo", credentials.getUserID());
+        assertEquals("bar", new String(credentials.getPassword()));
+    }
+
+    @Test
+    public void testMoreWhitespace() throws LoginException {
+        String b64 = 
Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
+        SimpleCredentials credentials = 
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic   " + b64)));
+        assertEquals("foo", credentials.getUserID());
+        assertEquals("bar", new String(credentials.getPassword()));
+    }
+
+    @Test(expected = LoginException.class)
+    public void testNonSpWhitespace() throws LoginException {
+        String b64 = 
Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
+        AuthorizationField.valueOf(Collections.enumeration(List.of("Basic \t " 
+ b64)));
+    }
+
+    @Test(expected = LoginException.class)
+    public void testBrokenBase64() throws LoginException {
+        String b64 = 
Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
+        // insert a single SP into the base64 sequence
+        b64 = b64.substring(0,5) + " " + b64.substring(5);
+        AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + 
b64)));
+    }
+
+    @Test(expected = LoginException.class)
+    public void testMoreBrokenBase64() throws LoginException {
+        String b64 = 
Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
+        b64 = b64.substring(0,5) + "=" + b64.substring(5);
+        AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + 
b64)));
+    }
+
+    @Test
+    public void testMoreNonAscii() throws LoginException {
+        String b64 = 
Base64.getEncoder().encodeToString("test:123\u00a3".getBytes(StandardCharsets.UTF_8));
+        SimpleCredentials credentials = 
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64)));
+        assertEquals("test", credentials.getUserID());
+        assertEquals("123\u00a3", new String(credentials.getPassword()));
+    }
+
+    @Test
+    public void testBasic64NoPadding() throws LoginException {
+        String b64 = 
Base64.getEncoder().withoutPadding().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
+        SimpleCredentials credentials = 
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64)));
         assertEquals("foo", credentials.getUserID());
-        // BUG: OAK-12259
         assertEquals("bar", new String(credentials.getPassword()));
     }
 }
\ No newline at end of file

Reply via email to