字符数组隐藏密码jdbc

时间:2015-05-28 16:15:39

标签: java arrays string jdbc connection

我试图在JDBC连接上实现最佳安全实践。 我读到实现密码的最佳方法是使用字符数组而不是字符串,因为字符串在Java中是不可变的。

唯一的问题是,我无法在Java API中找到任何支持隐藏char[]类型密码的方法。

有什么建议吗?

        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);      

1 个答案:

答案 0 :(得分:7)

首先,让我们明确为什么char[]String更适合密码。

如果您认为黑客可以转储JVM的内存映像,或者以任何其他方式搜索您的内存空间,那么只要有足够的时间,他们就可以在内存中找到密码。出于这个原因,最好将密码保存在内存中非常短的时间 - 从用户那里获取密码,将密码传递到需要它的地方,然后将其从内存中删除。

天真地,人们会这样做:

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

现在密码字符串(假设它是"myPass")已准备好进入垃圾回收。但它仍然在记忆中。人们永远不知道什么时候会收集它。也许永远不会,因为你的应用程序有足够的内存。即使收集它,也无法保证内存将被删除。对于黑客来说,这需要很长时间才能彻底搜索您的内存空间以查找可能的密码。

现在,您可能已经听说过字符数组是一个很好的解决方案。所以你用以下代码替换上面的天真代码:

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

嗯,不幸的是,同样的事情在这里也是如此。字符数组['m','y','P','a','s','s']仍然在内存中,它正在等待垃圾收集,它可能永远不会被删除。

因此字符数组不会自动保证更好的安全性。你实际上必须做一些事情:

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

现在,原始密码中的每个字符都已被删除并替换为零。该对象仍在等待垃圾收集,但其中不再有有意义的密码。

您无法直接对String执行此操作,因为实现它的char[]数组是私有的,您无法在其中设置字符 - 它是不可变的。所以它必须是char[]

现在我们知道了,规则很明确:它必须是一个char[]对象。也就是说,您无法将其转换为String沿途的任何位置,或克隆或复制它。如果你这样做 - 你失去了整个利益。

所以这部分代码:

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

实际上违反了这条规则。它在String中创建了一个sPassword对象,其中包含密码,我们失去了安全性。

这里真正的问题是连接到JDBC不允许您传递char[]的密码。 DriverManager.getConnection()方法期望String,而不是char[],无论您是将密码放在连接网址中,还是将其作为参数传递。

事实上,如果您希望保持这种安全级别以便通过JDBC进行连接,则必须找到另一种针对您的数据库进行身份验证的方法,该方法不使用DriverManager.getConnection(String, String, String)或{{1 }}

我没有测试它,但是MySQL提供了一种使用SSL和客户端证书进行身份验证的方法。可能还有其他方法使用Connector / J的其他身份验证插件。 但要求用户输入用户名和密码并使用DriverManager.getConnection(String)的简单方法是不允许您通过DriverManager.getConnection()维护密码安全。