2015-05-28 4 views
2

Я пытаюсь внедрить лучшие методы обеспечения безопасности в моем соединении JDBC. Я прочитал, что лучший способ реализовать пароль - использовать массив символов, в отличие от String, потому что Strings неизменяемы в Java.массив символов скрыть пароль jdbc

Единственная проблема заключается в том, что я не могу найти какой-либо метод в 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);  
+0

Почему бы не преобразовать его для скрытия? – Adam

+0

Что нужно сделать для обеспечения безопасности и защиты паролем? Почему вы хотите изменить объект пароля? – Alexander

+1

@ Александр - идея состоит в том, что пароль остается в памяти и не удаляется из него до сбора мусора, поэтому он достаточно долго доступен для хакеров, которые взломают ваше пространство памяти. Используя изменяемый объект, вы можете фактически обнулить память, как только закончите с ней. – RealSkeptic

ответ

6

Во-первых, давайте понять, почему 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[] в полном размере. То есть вы не можете преобразовать его в String в любом месте пути или клонировать или копировать его. Если вы это сделаете - вы потеряли всю пользу.

Так что этот кусок кода:

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

ли на самом деле нарушение этого правила.Он создает объект String в sPassword, который содержит пароль, и мы потеряли нашу безопасность.


Реальная проблема здесь состоит в том, что подключение к JDBC не позволяет передать пароль, который является char[]. Методы DriverManager.getConnection() ожидают String, а не char[], вводите ли вы пароль в URL-адрес соединения или передаете его в качестве параметра.

Таким образом, если вы хотите сохранить этот уровень безопасности для подключения через JDBC, вам нужно будет найти альтернативный метод аутентификации против вашей базы данных, который не использует DriverManager.getConnection(String, String, String) или DriverManager.getConnection(String).

Я не тестировал его, но MySQL предлагает способ аутентификации с использованием сертификата SSL с сертификатом клиента. Могут быть другие способы использования других модулей проверки подлинности для Connector/J. Но простой способ запросить у пользователя его имя пользователя и пароль и использовать DriverManager.getConnection() не позволит вам поддерживать защиту паролем через char[].

+0

Очень интересно. –

+0

Nice сообщение. Однако я не стал бы слишком беспокоиться об этом сценарии угроз - если злоумышленник может получить доступ к памяти JVM вашего сервера приложений, она, вероятно, также может прочитать пароль непосредственно из файла '* ds.xml' (или аналогичного), где он находится в обычном тексте. –

+0

Только что заметил, что пример OP был для приложения Swing (client), так что это может быть полезно, потому что pw нигде не хранится. –

Смежные вопросы