This is an automated email from the ASF dual-hosted git repository.
rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 67d1cef188 Reject BASIC authorization header with null password
67d1cef188 is described below
commit 67d1cef1887659c60c2804e153078c0edfaca2f3
Author: remm <[email protected]>
AuthorDate: Wed Jun 24 21:57:08 2026 +0200
Reject BASIC authorization header with null password
RFC 7617 mandates that a ':' must be present.
As there does not seem to be any adverse effects, I will leave it as
Tomcat 12 only.
---
.../catalina/authenticator/BasicAuthenticator.java | 10 ++++------
.../catalina/authenticator/LocalStrings.properties | 1 +
.../catalina/authenticator/TestBasicAuthParser.java | 18 ++++++++----------
webapps/docs/changelog.xml | 4 ++++
4 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/java/org/apache/catalina/authenticator/BasicAuthenticator.java
b/java/org/apache/catalina/authenticator/BasicAuthenticator.java
index a22d5779f4..eb93d5e4f8 100644
--- a/java/org/apache/catalina/authenticator/BasicAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/BasicAuthenticator.java
@@ -188,8 +188,7 @@ public class BasicAuthenticator extends AuthenticatorBase {
/**
* Trivial accessor.
*
- * @return the decoded password token as a String, or
<code>null</code> if no password was found in the
- * credentials.
+ * @return the decoded password token as a String, which is never
<code>null</code>, but can be empty.
*/
public String getPassword() {
return password;
@@ -228,7 +227,7 @@ public class BasicAuthenticator extends AuthenticatorBase {
}
/*
- * Extract the mandatory username token and separate it from the
optional password token. Tolerate surplus
+ * Extract the mandatory username and password tokens separated by a
colon. Tolerate surplus
* surrounding white space.
*/
private void parseCredentials(byte[] decoded) throws
IllegalArgumentException {
@@ -241,10 +240,9 @@ public class BasicAuthenticator extends AuthenticatorBase {
}
}
- // Tomcat allows a null password
+ // Null password is not allowed according to RFC 7617
if (colon < 0) {
- username = new String(decoded, charset);
- // password will remain null!
+ throw new
IllegalArgumentException(sm.getString("basicAuthenticator.noColon"));
} else {
username = new String(decoded, 0, colon, charset);
password = new String(decoded, colon + 1, decoded.length -
colon - 1, charset);
diff --git a/java/org/apache/catalina/authenticator/LocalStrings.properties
b/java/org/apache/catalina/authenticator/LocalStrings.properties
index b1f1e68c89..22ba239883 100644
--- a/java/org/apache/catalina/authenticator/LocalStrings.properties
+++ b/java/org/apache/catalina/authenticator/LocalStrings.properties
@@ -41,6 +41,7 @@ authenticator.userPermissionFail=User [{0}] does not have
authorization to acces
basicAuthenticator.invalidAuthorization=Invalid Authorization header
basicAuthenticator.invalidCharset=The only permitted values are null, the
empty string or UTF-8
+basicAuthenticator.noColon=Basic Authorization credentials do not contain a
colon
basicAuthenticator.notBase64=Basic Authorization credentials are not Base64
basicAuthenticator.notBasic=Authorization header method is not ''Basic''
diff --git a/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
b/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
index feb95f0f77..9d3e6aba92 100644
--- a/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
+++ b/test/org/apache/catalina/authenticator/TestBasicAuthParser.java
@@ -46,13 +46,12 @@ public class TestBasicAuthParser {
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
- @Test
- public void testGoodCredentialsNoPassword() throws Exception {
+ @Test(expected = IllegalArgumentException.class)
+ public void testBadCredentialsNoPassword() throws Exception {
final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD,
USER_NAME, null);
+ @SuppressWarnings("unused")
BasicAuthenticator.BasicCredentials credentials =
new
BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(),
StandardCharsets.UTF_8);
- Assert.assertEquals(USER_NAME, credentials.getUsername());
- Assert.assertNull(credentials.getPassword());
}
@Test
@@ -65,14 +64,13 @@ public class TestBasicAuthParser {
Assert.assertEquals(PASSWORD, credentials.getPassword());
}
- @Test
- public void testGoodCribUserOnly() throws Exception {
+ @Test(expected = IllegalArgumentException.class)
+ public void testBadCribUserOnly() throws Exception {
final String BASE64_CRIB = "dXNlcmlk";
final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD,
BASE64_CRIB);
+ @SuppressWarnings("unused")
BasicAuthenticator.BasicCredentials credentials =
new
BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(),
StandardCharsets.UTF_8);
- Assert.assertEquals(USER_NAME, credentials.getUsername());
- Assert.assertNull(credentials.getPassword());
}
@Test
@@ -108,8 +106,8 @@ public class TestBasicAuthParser {
// Our decoder accepts a long token without complaint.
// 80 characters
final String USER_LONG =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/AAAABBBBCCCCDDDD";
- final String BASE64_CRIB =
"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0" +
- "NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ="; // no new line
+ final String BASE64_CRIB =
"QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5e" +
+ "jAxMjM0NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ6"; // no new line
final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD,
BASE64_CRIB);
BasicAuthenticator.BasicCredentials credentials =
new
BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(),
StandardCharsets.UTF_8);
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index d5d2bceef3..cbc17e7130 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -217,6 +217,10 @@
implementations), along with version compatibility warnings and
third-party library version information. (csutherl)
</add>
+ <fix>
+ Reject BASIC authorization with no password, to comply with RFC 7617
+ strictly. (remm)
+ </fix>
<!-- Entries for backport and removal before 12.0.0-M1 below this line
-->
<fix>
Avoid a race condition with concurrent lookups for a singleton JNDI
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]