4

I'm trying to implement best security practices on my JDBC connection. I read that the best way to implement a password, is to use an array of characters, as opposed to a String, because Strings are immutable in Java.

The only problem is, I can't find any method in the Java API that supports hiding a password which is of type char[].

Any suggestions?

        import java.awt.Dimension;
        import java.awt.GridLayout;
        import java.awt.Toolkit;
        import java.sql.Connection;
        import java.sql.DriverManager;
        import java.sql.ResultSet;
        import java.sql.Statement;

        import javax.swing.Box;
        import javax.swing.JFrame;
        import javax.swing.JPanel;
        import javax.swing.JPasswordField;
        import javax.swing.JTable;
        import javax.swing.JTextField;
        import javax.swing.JOptionPane;
        import javax.swing.JLabel;

public class DbConnect
{
    public static void main(String args[])
    {

        try
        {
                Class.forName("com.mysql.jdbc.Driver");
                Connection conn = null;
                Statement stmt = null;
                String username = "";
                String sPassword = "";
                char[] cPassword;


                JTextField usernameField = new JTextField(50);
                JPasswordField passwordField = new JPasswordField(20);

                JPanel loginPanel = new JPanel();
                loginPanel.add(new JLabel("Username:"));
                loginPanel.add(usernameField);
                loginPanel.add(Box.createHorizontalStrut(15)); 
                loginPanel.add(new JLabel("Password:"));
                loginPanel.add(passwordField);

                  int result = JOptionPane.showConfirmDialog(null, loginPanel, 
                           "Please Enter Username and Password", JOptionPane.OK_CANCEL_OPTION);

                  if (result == JOptionPane.OK_OPTION) {
                     username = usernameField.getText();
                     cPassword = passwordField.getPassword();

                     //password = passwordField.getText();

                     for(int c = 0; c < cPassword.length; c++)
                         sPassword = sPassword + cPassword[c];

                /*conn = DriverManager.getConnection("jdbc:mysql://x.x.x.x");*/
                conn = DriverManager.getConnection("jdbc:mysql://x.x.x.x", username, sPassword);
                System.out.print("Database is connected\n");

                 //STEP 4: Execute a query
                  System.out.println("Creating statement...");
                  stmt = conn.createStatement();
                  String sql;
                  sql = "SELECT Fld1, Fld2, Fld3, Fld4 FROM db.tbl";
                  ResultSet rs = stmt.executeQuery(sql);      
Jud
  • 1,324
  • 3
  • 24
  • 47
TripWire
  • 71
  • 1
  • 10
  • Why not convert it for sake of hiding? – Adam May 28 '15 at 16:18
  • What does immutabiblity and password security have to do? Why do you want to change the password object? – Alexander May 28 '15 at 16:19
  • 1
    @Alexander - the idea is that the password stays in memory and is not removed from it until garbage-collected, so it's available for a long time for hackers who hack your memory space. Using a mutable object you can actually zero-out the memory as soon as you're done with it. – RealSkeptic May 28 '15 at 16:21
  • With the string immutable, it will be floating around in memory until the gc comes for it. If you set a char[] to null, you are able to destroy the object more quickly. – TripWire May 28 '15 at 16:27
  • 1
    @TripWire - setting it to null will do nothing compared to `String`. It's setting each element to `\u0000` or something that will do the trick. – RealSkeptic May 28 '15 at 16:28
  • Sorry, that's what I meant. But either way, you're able to do something with it. – TripWire May 28 '15 at 17:02
  • just for reference, a common and easier to read idiom to convert a `char[]` to a `String` is simply `String s = new String(charArray);` – fspinnenhirn May 28 '15 at 21:18

1 Answers1

8

First, let's make it clear why char[] is preferable to String for passwords.

If you assume that a hacker can dump memory images of your JVM, or search your memory space in any other way, then they can find passwords that are out there in memory, given enough time. For this reason, it's best to keep passwords in memory for very short durations - get the password from the user, pass it to wherever needs it, and then remove it from memory.

Naively, one would do something like this:

String password = getPasswordFromUser();
usePassword(password);
password = null;

Now the password string (suppose it's "myPass"), is ready to go to garbage collection. But it's still in memory. One never knows when it will be collected. Perhaps never, because you have enough memory for your application. And even when it is collected, there is no guarantee that the memory will be erased. This is a long time for the hacker to thoroughly search your memory space for likely passwords.

Now, you may have heard that character arrays are a good solution for this. So you replace the naive code above with:

char[] password = getPasswordFromUser(); // Now returning a character array
usePassword(password);
password = null;

Well, unfortunately, the same thing is true here. The character array ['m','y','P','a','s','s'] is still in memory, it's waiting for garbage collection, and it may never be erased.

So character arrays don't automatically guarantee better security. You actually have to do something like:

char[] password = getPasswordFromUser(); // Now returning a character array
usePassword(password);
Arrays.fill(password, '\u0000');
password = null;

Now each character from the original password has been erased and replaced with a zero. The object still waits for garbage collection, but there is no longer a meaningful password in it.

You can't do that directly to a String because the char[] array that implements it is private and you can't set the characters in it - it's immutable. So it has to be char[].

Now that we know that, the rule is clear: it has to be a single char[] object all the way. That is, you can't convert it into a String anywhere along the way, or clone or copy it. If you do - you lost the whole benefit.

So this piece of your code:

                for(int c = 0; c < cPassword.length; c++)
                     sPassword = sPassword + cPassword[c];

Is actually breaking that rule. It creates a String object in sPassword, that contains the password and we lost our security.


The real problem here is that connecting to JDBC does not allow you to pass a password that is a char[]. The DriverManager.getConnection() methods expect String, not char[], whether you put the password in the connection URL, or pass it as a parameter.

So in fact, if you want to keep this level of security for connecting over JDBC, you'll have to find an alternative method of authenticating against your database, that does not use the DriverManager.getConnection(String, String, String) or DriverManager.getConnection(String).

I did not test it, but MySQL offers a way to authenticate using SSL with a client certificate. There may be other ways using other authentication plugins for Connector/J. But the simple way of asking the user for his username and password and using DriverManager.getConnection() is not going to allow you to maintain password security via char[].

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • Nice post. However, I wouldn't be too worried about this threat scenario -- if the attacker can access the memory of your application server's JVM, she can probably also read the password directly from the `*ds.xml` file (or similar), where it's in plain text. – Mick Mnemonic May 28 '15 at 18:16
  • Just noticed that OP's example was for a Swing (client) app, so there this might actually be useful, because the pw is not stored anywhere. – Mick Mnemonic May 28 '15 at 18:18
  • @MickMnemonic just curious: what were you talking about before you noticed it was Swing? – RealSkeptic May 28 '15 at 18:29
  • I meant an application server's (Tomcat/JBoss etc.) JVM. The credentials of the DB are normally stored in plain text in the app server's installation directory. However, I think you can alleviate the problem by calling GC after connecting to the DB. Only string literals and `intern`ed strings should reside in the string pool according to [this thread](http://stackoverflow.com/questions/5457146/regarding-java-string-constant-pool). – Mick Mnemonic May 28 '15 at 18:36
  • @MickMnemonic Garbage collection doesn't ensure memory erasure. Actually, calling gc does not even guarantee that garbage collection will be called. For servers, if you really care about this sort of level of security, you'd probably be using certificates for authentication rather than user/password. Anyway, level of security is always a function of risk management, not everybody should be pursuing it to this level. – RealSkeptic May 28 '15 at 18:46
  • Very eloquently put RealSkeptic. Thanks to you and everyone else. I'll pursue a method that does not utilize Java authentication. – TripWire May 28 '15 at 20:52