Skip to content

Commit 970e615

Browse files
committed
Make timing attacks against the Realm implementations harder. (schultz)
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1758502 13f79535-47bb-0310-9956-ffa450edef68
1 parent ffa0346 commit 970e615

File tree

5 files changed

+91
-78
lines changed

5 files changed

+91
-78
lines changed

java/org/apache/catalina/realm/DataSourceRealm.java

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
* The ASF licenses this file to You under the Apache License, Version 2.0
66
* (the "License"); you may not use this file except in compliance with
77
* the License. You may obtain a copy of the License at
8-
*
8+
*
99
* http://www.apache.org/licenses/LICENSE-2.0
10-
*
10+
*
1111
* Unless required by applicable law or agreed to in writing, software
1212
* distributed under the License is distributed on an "AS IS" BASIS,
1313
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -271,13 +271,13 @@ public String getInfo() {
271271
*/
272272
@Override
273273
public Principal authenticate(String username, String credentials) {
274-
274+
275275
// No user or no credentials
276276
// Can't possibly authenticate, don't bother the database then
277277
if (username == null || credentials == null) {
278278
return null;
279279
}
280-
280+
281281
Connection dbConnection = null;
282282

283283
// Ensure that we have an open database connection
@@ -286,7 +286,7 @@ public Principal authenticate(String username, String credentials) {
286286
// If the db connection open fails, return "not authenticated"
287287
return null;
288288
}
289-
289+
290290
try
291291
{
292292
// Acquire a Principal object for this user
@@ -331,6 +331,8 @@ protected Principal authenticate(Connection dbConnection,
331331

332332
if(dbCredentials == null) {
333333
// User was not found in the database.
334+
// Waste a bit of time as not to reveal that the user does not exist.
335+
compareCredentials(credentials, getClass().getName());
334336

335337
if (containerLog.isTraceEnabled())
336338
containerLog.trace(sm.getString("dataSourceRealm.authenticateFailure",
@@ -374,7 +376,7 @@ protected void close(Connection dbConnection) {
374376
try {
375377
if (!dbConnection.getAutoCommit()) {
376378
dbConnection.commit();
377-
}
379+
}
378380
} catch (SQLException e) {
379381
containerLog.error("Exception committing connection before closing:", e);
380382
}
@@ -408,7 +410,7 @@ protected Connection open() {
408410
} catch (Exception e) {
409411
// Log the problem for posterity
410412
containerLog.error(sm.getString("dataSourceRealm.exception"), e);
411-
}
413+
}
412414
return null;
413415
}
414416

@@ -437,18 +439,18 @@ protected String getPassword(String username) {
437439
}
438440

439441
try {
440-
return getPassword(dbConnection, username);
442+
return getPassword(dbConnection, username);
441443
} finally {
442444
close(dbConnection);
443445
}
444446
}
445-
447+
446448
/**
447449
* Return the password associated with the given principal's user name.
448450
* @param dbConnection The database connection to be used
449451
* @param username Username for which password should be retrieved
450452
*/
451-
protected String getPassword(Connection dbConnection,
453+
protected String getPassword(Connection dbConnection,
452454
String username) {
453455

454456
ResultSet rs = null;
@@ -463,7 +465,7 @@ protected String getPassword(Connection dbConnection,
463465
}
464466

465467
return (dbCredentials != null) ? dbCredentials.trim() : null;
466-
468+
467469
} catch(SQLException e) {
468470
containerLog.error(
469471
sm.getString("dataSourceRealm.getPassword.exception",
@@ -480,10 +482,10 @@ protected String getPassword(Connection dbConnection,
480482
containerLog.error(
481483
sm.getString("dataSourceRealm.getPassword.exception",
482484
username), e);
483-
485+
484486
}
485487
}
486-
488+
487489
return null;
488490
}
489491

@@ -527,15 +529,15 @@ protected ArrayList<String> getRoles(String username) {
527529
close(dbConnection);
528530
}
529531
}
530-
532+
531533
/**
532534
* Return the roles associated with the given user name
533535
* @param dbConnection The database connection to be used
534536
* @param username Username for which roles should be retrieved
535537
*/
536538
protected ArrayList<String> getRoles(Connection dbConnection,
537539
String username) {
538-
540+
539541
if (allRolesMode != AllRolesMode.STRICT_MODE && !isRoleStoreDefined()) {
540542
// Using an authentication only configuration and no role store has
541543
// been defined so don't spend cycles looking
@@ -545,12 +547,12 @@ protected ArrayList<String> getRoles(Connection dbConnection,
545547
ResultSet rs = null;
546548
PreparedStatement stmt = null;
547549
ArrayList<String> list = null;
548-
550+
549551
try {
550552
stmt = roles(dbConnection, username);
551553
rs = stmt.executeQuery();
552554
list = new ArrayList<String>();
553-
555+
554556
while (rs.next()) {
555557
String role = rs.getString(1);
556558
if (role != null) {
@@ -576,7 +578,7 @@ protected ArrayList<String> getRoles(Connection dbConnection,
576578
username), e);
577579
}
578580
}
579-
581+
580582
return null;
581583
}
582584

@@ -600,7 +602,7 @@ private PreparedStatement credentials(Connection dbConnection,
600602
return (credentials);
601603

602604
}
603-
605+
604606
/**
605607
* Return a PreparedStatement configured to perform the SELECT required
606608
* to retrieve user roles for the specified username.
@@ -613,7 +615,7 @@ private PreparedStatement credentials(Connection dbConnection,
613615
private PreparedStatement roles(Connection dbConnection, String username)
614616
throws SQLException {
615617

616-
PreparedStatement roles =
618+
PreparedStatement roles =
617619
dbConnection.prepareStatement(preparedRoles);
618620

619621
roles.setString(1, username);
@@ -659,7 +661,7 @@ protected void startInternal() throws LifecycleException {
659661
temp.append(userNameCol);
660662
temp.append(" = ?");
661663
preparedCredentials = temp.toString();
662-
664+
663665
super.startInternal();
664666
}
665667
}

java/org/apache/catalina/realm/JDBCRealm.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,8 @@ public synchronized Principal authenticate(Connection dbConnection,
409409

410410
if (dbCredentials == null) {
411411
// User was not found in the database.
412+
// Waste a bit of time as not to reveal that the user does not exist.
413+
compareCredentials(credentials, getClass().getName());
412414

413415
if (containerLog.isTraceEnabled())
414416
containerLog.trace(sm.getString("jdbcRealm.authenticateFailure",

java/org/apache/catalina/realm/MemoryRealm.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
* The ASF licenses this file to You under the Apache License, Version 2.0
66
* (the "License"); you may not use this file except in compliance with
77
* the License. You may obtain a copy of the License at
8-
*
8+
*
99
* http://www.apache.org/licenses/LICENSE-2.0
10-
*
10+
*
1111
* Unless required by applicable law or agreed to in writing, software
1212
* distributed under the License is distributed on an "AS IS" BASIS,
1313
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -150,6 +150,10 @@ public Principal authenticate(String username, String credentials) {
150150
GenericPrincipal principal = principals.get(username);
151151

152152
if (principal == null || principal.getPassword() == null) {
153+
// User was not found in the database or the password was null
154+
// Waste a bit of time as not to reveal that the user does not exist.
155+
compareCredentials(credentials, getClass().getName());
156+
153157
if (log.isDebugEnabled())
154158
log.debug(sm.getString("memoryRealm.authenticateFailure", username));
155159
return null;

0 commit comments

Comments
 (0)