2009-06-11 12 views
26

スウィングのJPasswordFieldには、getPassword()メソッドがあり、char配列を返します。これを私が理解していることは、アレイを使用直後にゼロにすることができるため、長い間メモリに敏感なものがぶら下がっていないことです。パスワードを取得する古い方法は、getText()を使用することでした。これはStringオブジェクトを返しますが、廃止予定です。JPasswordField.getPassword()がパスワードを含むStringを作成するのはなぜですか?

私の質問は、なぜ実際にはJavaで検索処理中にgetPassword()を使用して使用されているのですか?より明確にするために、私は何かのために私のテストアプリケーションをデバッグしていました**、私はコールとバングに従いました... getText() in JPasswordFieldが呼び出されました、もちろん、私のパスワードを持つすてきなStringオブジェクトが作成され、想い出。

は自分自身でそれを試してみてください:

public class PasswordTest() { 
    public static void main(String[] args) { 
     JFrame frame = new JFrame(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     JPasswordField passField = new JPasswordField(); 
     pass.addActionListener(new ActionListener() { 
      public ActionPerformed(ActionEvent evt) { 
       char[] p = passField.getPassword(); // put breakpoint 
       // do something with the array 
      } 
     }); 
     frame.add(passField); 
     frame.setVisible(true); 
     frame.pack(); 
    } 
} 

は質問をフォローアップ:getText()のこの「隠された」使用はどのような方法で危険なのですか?もちろん、専用の攻撃者がシステムを侵害した場合はパスワードを取得しますが、私はそれほど献身的ではないと話しています;)

**私は実際にいくつかの機密データを表示する方法を探していましたStringオブジェクトを使用しないSwingコンポーネント私はSwing APIの一部(すべて?)を書き直すつもりでない限り、それを行う方法はありません。

+0

私は、機密データを表示するためにSwing APIを書き直す必要があると主張しています。 JComponentを拡張してpaintComponentをオーバーライドするカスタムコンポーネントを作成できますか?それでは、テキストの処理方法はあなた次第です。 –

答えて

22

実は、ここgetPassword()のSunの実装があります:

public char[] getPassword() { 
    Document doc = getDocument(); 
    Segment txt = new Segment(); 
    try { 
     doc.getText(0, doc.getLength(), txt); // use the non-String API 
    } catch (BadLocationException e) { 
     return null; 
    } 
    char[] retValue = new char[txt.count]; 
    System.arraycopy(txt.array, txt.offset, retValue, 0, txt.count); 
    return retValue; 
} 

そこに唯一getTextはターンコピー文字で直接SegmentのバッファにgetChars(int where, int len, Segment txt)を呼び出しgetText(int offset, int length, Segment txt)への呼び出しは、あります。そこにはStringsが作成されていません。

次に、メソッドが戻る前に、Segmentのバッファが戻り値にコピーされ、ゼロになっています。

他の言葉:パスワードの余分なコピーはどこにもありません。。指示どおりに使用する限り、完全に安全です。

+0

私にそれを打つ! –

+0

私は驚いています。スウィングコードを突き止めるのに15分かかりましたが、何事も起こっていないことを自分に保証します。 –

+0

私もそれをチェックしました... –

4

Swingの実装は複雑すぎて手作業で確認できません。あなたはテストをしたい。

public class Pwd { 
    public static void main(String[] args) { 
     java.awt.EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       new javax.swing.JFrame("Pwd") {{ 
        add(new javax.swing.JPasswordField() { 
         @Override public String getText() { 
          System.err.println("Awoooga!!"); 
          return super.getText(); 
         } 
         { 
          addActionListener(
           new java.awt.event.ActionListener() { 
            public void actionPerformed(
             java.awt.event.ActionEvent event 
            ) { 
             // Nice. 
            } 
           } 
          ); 
         } 
        }); 
        setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
        pack(); 
        setVisible(true); 
       }}; 
      } 
     }); 
    } 
} 

(無意味な)アクションイベントのコマンド文字列に似ています。その効果を引き起こす別の方法もあります。

漠然と現代のVMはそうchar[]は必ずしも動作しないクリア、とにかくメモリ内のオブジェクトを移動します。

+0

"ぼんやりとしたモダンなVMは、とにかくメモリ内のオブジェクトを移動させるので、char []のクリアは必ずしも機能しません。それは非常に良い点です。 +1だけで、残りの部分を読む前に。 –

+0

移動GCがchar []を無効にするのはなぜですか? char []がパスワードで書かれた瞬間に可能であると言っていますが、クリアされる前にGCが実行され、配列のどこかに配列がコピーされ、パスワードの元のコピーは上書きされる "ゴミ"後で割り当てることによって? –

+1

@Jasonはい。私は、典型的な実装では、割り振り時にそれを行うよりも、ゼロメモリ(少なくとも "Eden"のような空間)をブロックすると考えています。コード・コックアップが実際の問題を引き起こす可能性がより高くなります - アクション・ソース、ドキュメントをクリアすることを覚えましたか?getTextは余分な 'char []'を作成します(しかし、あなたはそれをクリアできません。ドキュメントの実装は、バッファなどを再割り当てすることがあります。 –

5

[OK]を、私の悪いです...

import java.awt.event.*; 

import javax.swing.*; 

public class PasswordTest { 
     public static void main(String[] args) { 
      JFrame frame = new JFrame(); 
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      final JPasswordField passField = new JPasswordField() { 
       @Override 
       public String getText() { 
        System.err.println("Awhooa: " + super.getText()); //breakpoint 
        return null; 
       } 
      }; 
      passField.addActionListener(new ActionListener() { 
       public void actionPerformed(ActionEvent evt) { 
        char[] p = passField.getPassword(); 
        System.out.println(p); 
       } 
      }); 
      frame.add(passField); 
      frame.setVisible(true); 
      frame.pack(); 
     } 
    } 
:すべての鐘がここにここでスタックトレース

PasswordTest$1.getText() line: 14 
PasswordTest$1(JTextField).fireActionPerformed() line: not available  
PasswordTest$1(JTextField).postActionEvent() line: not available  
JTextField$NotifyAction.actionPerformed(ActionEvent) line: not available  
SwingUtilities.notifyAction(Action, KeyStroke, KeyEvent, Object, int) line: not available 

は、コード使用されていますが実際にアクションリスナーで私が導入されたことに気づかず)とすぐに私はのgetText(への呼び出しを見たように鳴り始めました ​​

とgetPasswordに()への実際の呼び出しのための

が、多分私は何かが欠けていますが、セグメントのバッファがゼロにされる:

そしてここでは、コンソール出力のですか?配列のコピーは見えますが、ゼロ点はありません。返される配列は、自分でゼロにされますが、セグメントの配列がまだそこにある...

import java.util.Arrays; 

public class ZeroingTest { 
    public static void main(String[] args) { 
     char[] a = {'a','b','c'}; 
     char[] b = new char[a.length]; 
     System.arraycopy(a, 0, b, 0, b.length); 
     System.out.println("Before zeroing: " + Arrays.toString(a) + " " + Arrays.toString(b)); 
     Arrays.fill(a, '\0'); 
     System.out.println("After zeroing: " + Arrays.toString(a) + " " + Arrays.toString(b)); 
    } 
} 

そして出力:

Before zeroing: [a, b, c] [a, b, c] 
After zeroing: [?, ?, ?] [a, b, c] 

(私は過去の印刷できない文字ができませんので、私はそこに疑問符を置く)

-M

+1

Segment char []についての良い点。 –

+1

あなたはそうです。セグメントのバッファがゼロになるのを見ていると思っていましたが、そうではありません。 Sunにバグを報告する必要があります。 –

+0

まだ6年後のJava 8と同じです。セグメントはクリアされず、GCが処理できるように残っています。 –

2

私は実際にいくつかの機密データを表示する方法を探していた間、**私はこれに出くわしましたStringオブジェクトを使用しないSwingコンポーネント私はSwing APIの一部(すべて?)を書き直すつもりでない限り、それを行う方法はありません。

JPasswordFieldには、field.setEchoChar('\0')を呼び出して文字を表示するように指示できます。これは、JPasswordFieldString秒、切り取り/コピーなし)によって提供される残りの保護を保持します。

28

これが私の作品:

コード:

String passText = new String(passField.getPassword()); 
+11

これで、パスワードをchar配列にする目的を完全に破っています。 –

+2

@A.Grandtあなたのコメントは技術的に非常に正確ですが、それはまったく役に立たないものです。パスワード検証は通常、データベースに対して行われ、ほとんどの場合文字列として送信されます。たとえば、JPA createQueryのsetParameter(文字列名、文字列値)コンテキストでchar配列を使用する方法を、可能な限り文字列を使用せずに説明することができます。 –

+3

@Tuncay私は心から正しい人にパスワードをデータベースにクリアテキストで保存するよう提案していないことを心から祈っていますか?そうすることは理解の上では狂っている。パスワードは、少なくともPBKDF2WithHmacSHA1またはPBKDF2WithHmacSHA256を使用し、反復回数が多く、パスワードごとに個別にランダムに生成されるソルト値を使用して、自家製コードではない安全な方法で完全にハッシュされます。これらのすべては、使用後にコンテンツを安全に消去できるため、バイトまたは文字配列として発生します。ハッシュされたパスワードのみが文字列に表示されるか、または文字列に表示される必要があります。 –

0
import javax.swing.*; 

public class login extends javax.swing.JFrame { 


MainProg main = new MainProg(); 

    public login() { 
     initComponents(); 
    } 

    /** 
    * This method is called from within the constructor to initialize the form. 
    * WARNING: Do NOT modify this code. The content of this method is always 
    * regenerated by the Form Editor. 
    */ 
    @SuppressWarnings("unchecked") 
    // <editor-fold defaultstate="collapsed" desc="Generated Code">       
    private void initComponents() { 

     jLabel1 = new javax.swing.JLabel(); 
     jLabel2 = new javax.swing.JLabel(); 
     txtUser = new javax.swing.JTextField(); 
     txtPassword = new javax.swing.JTextField(); 
     jButton1 = new javax.swing.JButton(); 

     setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 
     setTitle("Log In"); 
     setBackground(new java.awt.Color(255, 204, 204)); 
     setResizable(false); 

     jLabel1.setText("Username:"); 

     jLabel2.setText("Password:"); 

     jButton1.setBackground(new java.awt.Color(204, 204, 204)); 
     jButton1.setText("Enter"); 
     jButton1.setOpaque(false); 
     jButton1.addActionListener(new java.awt.event.ActionListener() { 
      public void actionPerformed(java.awt.event.ActionEvent evt) { 
       jButton1ActionPerformed(evt); 
      } 
     }); 

     javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 
     getContentPane().setLayout(layout); 
     layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 
      .addGroup(layout.createSequentialGroup() 
       .addContainerGap() 
       .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) 
        .addComponent(jButton1) 
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) 
         .addGroup(layout.createSequentialGroup() 
          .addComponent(jLabel1) 
          .addGap(18, 18, 18) 
          .addComponent(txtUser, javax.swing.GroupLayout.PREFERRED_SIZE, 210, javax.swing.GroupLayout.PREFERRED_SIZE)) 
         .addGroup(layout.createSequentialGroup() 
          .addComponent(jLabel2) 
          .addGap(20, 20, 20) 
          .addComponent(txtPassword)))) 
       .addContainerGap(62, Short.MAX_VALUE)) 
     ); 
     layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 
      .addGroup(layout.createSequentialGroup() 
       .addContainerGap() 
       .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 
        .addComponent(jLabel1) 
        .addComponent(txtUser, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 
       .addGap(18, 18, 18) 
       .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 
        .addComponent(jLabel2) 
        .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 
       .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 
       .addComponent(jButton1) 
       .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 
     ); 

     pack(); 
    }// </editor-fold>       

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {           
     String U = new String(this.txtUser.getText()); 
     String P = new String(this.txtPass.gettext()); 


     if(U.equals("Admin") && P.equals(Password)) 
     { 
      JOptionPane.showMessageDialog(null,"Login successful!","Message",JOptionPane.INFORMATION_MESSAGE); 
      this.hide(); 
      calculator.show(); 
     } 
     else 
     { 
      JOptionPane.showMessageDialog(null,"Invalid username and password","Message",JOptionPane.ERROR_MESSAGE); 
      this.txtUser.setText(""); 
      this.txtPassword.setText("");      
     } 

    }           

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String args[]) { 
     /* 
     * Set the Nimbus look and feel 
     */ 
     //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> 
     /* 
     * If Nimbus (introduced in Java SE 6) is not available, stay with the 
     * default look and feel. For details see 
     * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */ 
     try { 
      for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { 
       if ("Nimbus".equals(info.getName())) { 
        javax.swing.UIManager.setLookAndFeel(info.getClassName()); 
        break; 
       } 
      } 
     } catch (ClassNotFoundException ex) { 
      java.util.logging.Logger.getLogger(login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 
     } catch (InstantiationException ex) { 
      java.util.logging.Logger.getLogger(login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 
     } catch (IllegalAccessException ex) { 
      java.util.logging.Logger.getLogger(login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 
     } catch (javax.swing.UnsupportedLookAndFeelException ex) { 
      java.util.logging.Logger.getLogger(login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 
     } 
     //</editor-fold> 

     /* 
     * Create and display the form 
     */ 
     java.awt.EventQueue.invokeLater(new Runnable() { 

      public void run() { 
       new login().setVisible(true); 
      } 
     }); 
    } 
    // Variables declaration - do not modify      
    private javax.swing.JButton jButton1; 
    private javax.swing.JLabel jLabel1; 
    private javax.swing.JLabel jLabel2; 
    private javax.swing.JTextField txtPassword; 
    private javax.swing.JTextField txtUser; 
    // End of variables declaration     
} 
+1

お手伝いをしてくれてありがとうございます:-)残念ながら、あなたのコードは、OPによって引き起こされた基本的な問題(getPasswordの呼び出しがセキュリティホールを開くかどうか)を扱っていません。ところで、Javaの命名規則を覚えておいてください。 – kleopatra

-2

これは私のために動作します。

String.valueOf(txtPass.getPassword()) 
+0

パスワードを扱うための鍵は、char/byte配列でそれらを保持することです。文字列に変換すると、その文字列はJava文字列内容は変更できません。文字列を変更するたびにメモリに新しいコピーが作成されます。配列を反復して消去するか、Arrays.fill(array、value)を使用すると、メモリ内の実際のデータが上書きされ、使用後に復元不能になります。 –

関連する問題