Skip to content

Commit

Permalink
Merge pull request #3833 from katzyn/password
Browse files Browse the repository at this point in the history
Disallow plain webAdminPassword values to force usage of hashes
  • Loading branch information
katzyn committed Jul 3, 2023
2 parents 578cb6d + 23ee3d0 commit 581ed18
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 16 deletions.
4 changes: 3 additions & 1 deletion h2/src/docsrc/html/tutorial.html
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,9 @@ <h2 id="console_settings">Settings of the H2 Console</h2>
<ul><li><code>webAllowOthers</code>: allow other computers to connect.
</li><li><code>webPort</code>: the port of the H2 Console
</li><li><code>webSSL</code>: use encrypted TLS (HTTPS) connections.
</li><li><code>webAdminPassword</code>: password to access preferences and tools of H2 Console.
</li><li><code>webAdminPassword</code>: hash of password to access preferences and tools of H2 Console,
use <code>org.h2.server.web.WebServer.encodeAdminPassword(String)</code> to generate a hash for your password.
Always use long complex passwords, especially when access from other hosts is enabled.
</li></ul>
<p>
In addition to those settings, the properties of the last recently used connection
Expand Down
5 changes: 2 additions & 3 deletions h2/src/main/org/h2/api/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -1890,9 +1890,8 @@ public class ErrorCode {
/**
* The error with code <code>90125</code> is thrown when
* PreparedStatement.setBigDecimal is called with object that extends the
* class BigDecimal, and the system property h2.allowBigDecimalExtensions is
* not set. Using extensions of BigDecimal is dangerous because the database
* relies on the behavior of BigDecimal. Example of wrong usage:
* class BigDecimal. Using extensions of BigDecimal is dangerous because the
* database relies on the behavior of BigDecimal. Example of wrong usage:
* <pre>
* BigDecimal bd = new MyDecimal("$10.3");
* prep.setBigDecimal(1, bd);
Expand Down
5 changes: 5 additions & 0 deletions h2/src/main/org/h2/engine/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,11 @@ public class Constants {
*/
public static final int QUERY_STATISTICS_MAX_ENTRIES = 100;

/**
* The minimum number of characters in web admin password.
*/
public static final int MIN_WEB_ADMIN_PASSWORD_LENGTH = 12;

/**
* Announced version for PgServer.
*/
Expand Down
29 changes: 22 additions & 7 deletions h2/src/main/org/h2/server/web/WebServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public class WebServer implements Service {
private String externalNames;
private boolean isDaemon;
private final Set<WebThread> running =
Collections.synchronizedSet(new HashSet<WebThread>());
Collections.synchronizedSet(new HashSet<>());
private boolean ssl;
private byte[] adminPassword;
private final HashMap<String, ConnectionInfo> connInfoMap = new HashMap<>();
Expand Down Expand Up @@ -926,17 +926,32 @@ void setAdminPassword(String password) {
adminPassword = null;
return;
}
if (password.length() == 128) {
try {
adminPassword = StringUtils.convertHexToBytes(password);
return;
} catch (Exception ex) {}
if (password.length() != 128) {
throw new IllegalArgumentException(
"Use result of org.h2.server.web.WebServer.encodeAdminPassword(String)");
}
adminPassword = StringUtils.convertHexToBytes(password);
}

/**
* Generates a random salt and returns it with a hash of specified password
* with this salt.
*
* @param password
* the password
* @return a salt and hash of salted password as a hex encoded string to be
* used in configuration file
* @throws IllegalArgumentException when password is too short
*/
public static String encodeAdminPassword(String password) {
if (password.length() < Constants.MIN_WEB_ADMIN_PASSWORD_LENGTH) {
throw new IllegalArgumentException("Min length: " + Constants.MIN_WEB_ADMIN_PASSWORD_LENGTH);
}
byte[] salt = MathUtils.secureRandomBytes(32);
byte[] hash = SHA256.getHashWithSalt(password.getBytes(StandardCharsets.UTF_8), salt);
byte[] total = Arrays.copyOf(salt, 64);
System.arraycopy(hash, 0, total, 32, 32);
adminPassword = total;
return StringUtils.convertBytesToHex(total);
}

/**
Expand Down
17 changes: 15 additions & 2 deletions h2/src/main/org/h2/tools/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class Server extends Tool implements Runnable, ShutdownHandler {
private final Service service;
private Server web, tcp, pg;
private ShutdownHandler shutdownHandler;
private boolean fromCommandLine;
private boolean started;

public Server() {
Expand Down Expand Up @@ -75,7 +76,11 @@ public Server(Service service, String... args) throws SQLException {
* <tr><td>[-webSSL]</td>
* <td>Use encrypted (HTTPS) connections</td></tr>
* <tr><td>[-webAdminPassword]</td>
* <td>Password of DB Console administrator</td></tr>
* <td>Hash of password of DB Console administrator, can be generated with
* {@linkplain WebServer#encodeAdminPassword(String)}. Can be passed only to
* the {@link #runTool(String...)} method, this method rejects it. It is
* also possible to store this setting in configuration file of H2
* Console.</td></tr>
* <tr><td>[-browser]</td>
* <td>Start a browser connecting to the web server</td></tr>
* <tr><td>[-tcp]</td>
Expand Down Expand Up @@ -123,7 +128,9 @@ public Server(Service service, String... args) throws SQLException {
* @throws SQLException on failure
*/
public static void main(String... args) throws SQLException {
new Server().runTool(args);
Server server = new Server();
server.fromCommandLine = true;
server.runTool(args);
}

private void verifyArgs(String... args) throws SQLException {
Expand All @@ -146,6 +153,9 @@ private void verifyArgs(String... args) throws SQLException {
} else if ("-webPort".equals(arg)) {
i++;
} else if ("-webAdminPassword".equals(arg)) {
if (fromCommandLine) {
throwUnsupportedOption(arg);
}
i++;
} else {
throwUnsupportedOption(arg);
Expand Down Expand Up @@ -249,6 +259,9 @@ public void runTool(String... args) throws SQLException {
} else if ("-webPort".equals(arg)) {
i++;
} else if ("-webAdminPassword".equals(arg)) {
if (fromCommandLine) {
throwUnsupportedOption(arg);
}
i++;
} else {
showUsageAndThrowUnsupportedOption(arg);
Expand Down
22 changes: 20 additions & 2 deletions h2/src/test/org/h2/test/server/TestWeb.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcSQLFeatureNotSupportedException;
import org.h2.jdbc.JdbcSQLNonTransientException;
import org.h2.server.web.WebServer;
import org.h2.server.web.WebServlet;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
Expand Down Expand Up @@ -159,18 +162,33 @@ private void testTools() throws Exception {
conn.createStatement().execute(
"create table test(id int) as select 1");
conn.close();
String hash = WebServer.encodeAdminPassword("1234567890AB");
try {
Server.main("-web", "-webPort", "8182",
"-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", hash);
fail("Expected exception");
} catch (JdbcSQLFeatureNotSupportedException e) {
// Expected
}
Server server = new Server();
server.setOut(new PrintStream(new ByteArrayOutputStream()));
try {
server.runTool("-web", "-webPort", "8182",
"-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", "123");
fail("Expected exception");
} catch (JdbcSQLNonTransientException e) {
// Expected
}
server.runTool("-web", "-webPort", "8182",
"-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", "123");
"-properties", "null", "-tcp", "-tcpPort", "9101", "-webAdminPassword", hash);
try {
String url = "http://localhost:8182";
WebClient client;
String result;
client = new WebClient();
result = client.get(url);
client.readSessionId(result);
result = client.get(url, "adminLogin.do?password=123");
result = client.get(url, "adminLogin.do?password=1234567890AB");
result = client.get(url, "tools.jsp");
FileUtils.delete(getBaseDir() + "/backup.zip");
result = client.get(url, "tools.do?tool=Backup&args=-dir," +
Expand Down
2 changes: 1 addition & 1 deletion h2/src/tools/org/h2/build/doc/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -854,4 +854,4 @@ allotted mismatched wise terminator guarding revolves notion piece submission re
duplicating unnested hardening sticky massacred
bck clo cur hwm materializedview udca vol connectionpooldatasource xadatasource
ampm sssssff sstzh tzs yyyysssss newsequentialid solidus openjdk furthermore ssff secons nashorn fractions
btrim underscores ffl decomposed decomposition subfield infinities retryable
btrim underscores ffl decomposed decomposition subfield infinities retryable salted

0 comments on commit 581ed18

Please sign in to comment.