This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new f5207843dafe camel-netty-http: decode Basic auth credentials as UTF-8
(#29711)
f5207843dafe is described below
commit f5207843dafeb5858c45830ac1adb55c138b7960
Author: dhairya05 <[email protected]>
AuthorDate: Thu Nov 12 23:39:09 2026 +0530
camel-netty-http: decode Basic auth credentials as UTF-8 (#29711)
---
.../http/handlers/HttpServerChannelHandler.java | 33 +++++-----
.../HttpServerChannelHandlerBasicAuthTest.java | 75 ++++++++++++++++++++++
2 files changed, 93 insertions(+), 15 deletions(-)
diff --git
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
index a3a57ffb4db4..7a004ff7f020 100644
---
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
+++
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
@@ -18,8 +18,8 @@ package org.apache.camel.component.netty.http.handlers;
import java.net.URI;
import java.nio.channels.ClosedChannelException;
-import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.Base64;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
@@ -30,7 +30,6 @@ import javax.security.auth.login.LoginException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderResult;
-import io.netty.handler.codec.base64.Base64;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
@@ -379,21 +378,25 @@ public class HttpServerChannelHandler extends
ServerChannelHandler {
String constraint = StringHelper.before(auth, " ");
if (constraint != null) {
if ("Basic".equalsIgnoreCase(constraint.trim())) {
- String decoded = StringHelper.after(auth, " ");
- // the decoded part is base64 encoded, so we need to
decode that
- ByteBuf buf =
NettyConverter.toByteBuffer(decoded.getBytes());
- ByteBuf out = Base64.decode(buf);
+ String encoded = StringHelper.after(auth, " ");
+ byte[] credentials;
try {
- String userAndPw =
out.toString(Charset.defaultCharset());
- String username = StringHelper.before(userAndPw, ":");
- String password = StringHelper.after(userAndPw, ":");
- HttpPrincipal principal = new HttpPrincipal(username,
password);
- LOG.debug("Extracted Basic Auth principal from HTTP
header: {}", principal);
- return principal;
- } finally {
- buf.release();
- out.release();
+ // the value is base64 encoded, so we need to decode
that
+ credentials =
Base64.getDecoder().decode(encoded.trim());
+ } catch (IllegalArgumentException e) {
+ // a client sent something that is not base64 at all:
no principal, so the request is
+ // answered with 401 rather than an error
+ LOG.debug("Cannot decode the Basic Auth header, it is
not valid base64");
+ return null;
}
+ // RFC 7617: the credentials are encoded with UTF-8, so
the platform default charset must not
+ // decide how a non-ASCII user name or password is read
back
+ String userAndPw = new String(credentials,
StandardCharsets.UTF_8);
+ String username = StringHelper.before(userAndPw, ":");
+ String password = StringHelper.after(userAndPw, ":");
+ HttpPrincipal principal = new HttpPrincipal(username,
password);
+ LOG.debug("Extracted Basic Auth principal from HTTP
header: {}", principal);
+ return principal;
}
}
}
diff --git
a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandlerBasicAuthTest.java
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandlerBasicAuthTest.java
new file mode 100644
index 000000000000..e64d2184ddb7
--- /dev/null
+++
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandlerBasicAuthTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.netty.http.handlers;
+
+import io.netty.handler.codec.http.DefaultHttpRequest;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpVersion;
+import org.apache.camel.component.netty.http.HttpPrincipal;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * RFC 7617 encodes the Basic credentials with UTF-8, so what the server reads
back must not depend on the JVM's default
+ * charset.
+ */
+class HttpServerChannelHandlerBasicAuthTest {
+
+ @Test
+ void shouldDecodeCredentialsAsUtf8() {
+ // "user:pässword" base64-encoded from its UTF-8 bytes, the way a
client sends it
+ HttpPrincipal principal = extract("Basic dXNlcjpww6Rzc3dvcmQ=");
+
+ assertNotNull(principal);
+ assertEquals("user", principal.getName());
+ assertEquals("pässword", principal.getPassword());
+ }
+
+ @Test
+ void shouldDecodeAsciiCredentials() {
+ // "scott:tiger"
+ HttpPrincipal principal = extract("Basic c2NvdHQ6dGlnZXI=");
+
+ assertNotNull(principal);
+ assertEquals("scott", principal.getName());
+ assertEquals("tiger", principal.getPassword());
+ }
+
+ @Test
+ void shouldIgnoreAnotherScheme() {
+ assertNull(extract("Bearer c2NvdHQ6dGlnZXI="));
+ }
+
+ @Test
+ void shouldIgnoreAValueThatIsNotBase64() {
+ // a client can send anything: no principal, so the request is
answered with 401 rather than an error.
+ // Note the JDK decoder does not require the trailing padding, so an
unpadded value is still decoded
+ assertNull(extract("Basic %%%"));
+ assertNull(extract("Basic not base64!"));
+ }
+
+ private static HttpPrincipal extract(String authorization) {
+ HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "/");
+ request.headers().set("Authorization", authorization);
+ return HttpServerChannelHandler.extractBasicAuthSubject(request);
+ }
+
+}