Skip to content

Commit 67d1cef

Browse files
committed
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.
1 parent 6218c05 commit 67d1cef

4 files changed

Lines changed: 17 additions & 16 deletions

File tree

java/org/apache/catalina/authenticator/BasicAuthenticator.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ public String getUsername() {
188188
/**
189189
* Trivial accessor.
190190
*
191-
* @return the decoded password token as a String, or <code>null</code> if no password was found in the
192-
* credentials.
191+
* @return the decoded password token as a String, which is never <code>null</code>, but can be empty.
193192
*/
194193
public String getPassword() {
195194
return password;
@@ -228,7 +227,7 @@ private byte[] parseBase64() throws IllegalArgumentException {
228227
}
229228

230229
/*
231-
* Extract the mandatory username token and separate it from the optional password token. Tolerate surplus
230+
* Extract the mandatory username and password tokens separated by a colon. Tolerate surplus
232231
* surrounding white space.
233232
*/
234233
private void parseCredentials(byte[] decoded) throws IllegalArgumentException {
@@ -241,10 +240,9 @@ private void parseCredentials(byte[] decoded) throws IllegalArgumentException {
241240
}
242241
}
243242

244-
// Tomcat allows a null password
243+
// Null password is not allowed according to RFC 7617
245244
if (colon < 0) {
246-
username = new String(decoded, charset);
247-
// password will remain null!
245+
throw new IllegalArgumentException(sm.getString("basicAuthenticator.noColon"));
248246
} else {
249247
username = new String(decoded, 0, colon, charset);
250248
password = new String(decoded, colon + 1, decoded.length - colon - 1, charset);

java/org/apache/catalina/authenticator/LocalStrings.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ authenticator.userPermissionFail=User [{0}] does not have authorization to acces
4141

4242
basicAuthenticator.invalidAuthorization=Invalid Authorization header
4343
basicAuthenticator.invalidCharset=The only permitted values are null, the empty string or UTF-8
44+
basicAuthenticator.noColon=Basic Authorization credentials do not contain a colon
4445
basicAuthenticator.notBase64=Basic Authorization credentials are not Base64
4546
basicAuthenticator.notBasic=Authorization header method is not ''Basic''
4647

test/org/apache/catalina/authenticator/TestBasicAuthParser.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,12 @@ public void testGoodCredentials() throws Exception {
4646
Assert.assertEquals(PASSWORD, credentials.getPassword());
4747
}
4848

49-
@Test
50-
public void testGoodCredentialsNoPassword() throws Exception {
49+
@Test(expected = IllegalArgumentException.class)
50+
public void testBadCredentialsNoPassword() throws Exception {
5151
final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, null);
52+
@SuppressWarnings("unused")
5253
BasicAuthenticator.BasicCredentials credentials =
5354
new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
54-
Assert.assertEquals(USER_NAME, credentials.getUsername());
55-
Assert.assertNull(credentials.getPassword());
5655
}
5756

5857
@Test
@@ -65,14 +64,13 @@ public void testGoodCrib() throws Exception {
6564
Assert.assertEquals(PASSWORD, credentials.getPassword());
6665
}
6766

68-
@Test
69-
public void testGoodCribUserOnly() throws Exception {
67+
@Test(expected = IllegalArgumentException.class)
68+
public void testBadCribUserOnly() throws Exception {
7069
final String BASE64_CRIB = "dXNlcmlk";
7170
final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
71+
@SuppressWarnings("unused")
7272
BasicAuthenticator.BasicCredentials credentials =
7373
new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);
74-
Assert.assertEquals(USER_NAME, credentials.getUsername());
75-
Assert.assertNull(credentials.getPassword());
7674
}
7775

7876
@Test
@@ -108,8 +106,8 @@ public void testGoodCribBase64Big() throws Exception {
108106
// Our decoder accepts a long token without complaint.
109107
// 80 characters
110108
final String USER_LONG = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/AAAABBBBCCCCDDDD";
111-
final String BASE64_CRIB = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0" +
112-
"NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ="; // no new line
109+
final String BASE64_CRIB = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5e" +
110+
"jAxMjM0NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ6"; // no new line
113111
final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB);
114112
BasicAuthenticator.BasicCredentials credentials =
115113
new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8);

webapps/docs/changelog.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@
217217
implementations), along with version compatibility warnings and
218218
third-party library version information. (csutherl)
219219
</add>
220+
<fix>
221+
Reject BASIC authorization with no password, to comply with RFC 7617
222+
strictly. (remm)
223+
</fix>
220224
<!-- Entries for backport and removal before 12.0.0-M1 below this line -->
221225
<fix>
222226
Avoid a race condition with concurrent lookups for a singleton JNDI

0 commit comments

Comments
 (0)