为什么JPasswordField.getPassword()会创建一个包含密码的String?

27 java security passwords swing

Swing的JPasswordFieldgetPassword()一个返回字符数组的方法.我对此的理解是,数组可以在使用后立即归零,这样你就不会在内存中长时间存在敏感的东西.检索密码的旧方法是使用getText(),它返回一个String对象,但它已被弃用.

所以,我的问题是为什么Java在检索过程中实际使用它getPassword()?为了更清楚,我正在调试我的测试应用程序的其他东西**,我跟着调用和爆炸... getText()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();
    }
}
Run Code Online (Sandbox Code Playgroud)

跟进问题:这种"隐藏"的getText()危险用途是什么?当然,如果一个专门的攻击者破坏了系统,它会获得你的密码,我说的是一个不那么专注的密码;)

**我在寻找一种在不使用String对象的情况下在Swing组件上实际显示一些敏感数据的方法时遇到了这个问题.显然除非我愿意重写Swing API的部分(全部?),否则无法做到这一点.不会发生.

Ali*_*oud 38

这对我有用,可以帮助您构建一个Stringified密码:

String passText = new String(passField.getPassword());
Run Code Online (Sandbox Code Playgroud)

  • 因此,您彻底违背了在char数组中使用密码的全部目的. (14认同)
  • @Tuncay我真诚地希望你不是建议任何心智正常的人在他们的数据库中以明文形式存储密码?这样做是疯狂的,无法理解.密码将使用安全的方式进行彻底的散列,如在非自制的代码中,至少使用PBKDF2WithHmacSHA1或PBKDF2WithHmacSHA256,具有大量迭代,并且为每个密码单独随机生成盐值.所有这些都发生在字节或字符数组中,因为您可以在使用后安全地清除其内容.只有散列密码可以或应该以字符串形式显示. (3认同)
  • @ A.Grandt虽然你的评论在技术上非常正确,但也完全没用.密码验证通常针对数据库进行,并且几乎总是作为字符串发送.例如,您可能会解释如何在JPA createQuery setParameter(字符串名称,字符串值)上下文中使用char数组,而不使用字符串(如果可能的话). (2认同)
  • @ A.Grant当然不是.我只是指出,沿线的某一点你几乎肯定需要一根绳子.例如,sha256,或加密它,或base64它... (2认同)

Mic*_*ers 23

实际上,这是Sun的实现getPassword():

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;
}
Run Code Online (Sandbox Code Playgroud)

唯一getText有一个调用getText(int offset, int length, Segment txt),调用getChars(int where, int len, Segment txt),然后将字符直接复制到Segment缓冲区.那里没有Strings被创造.

然后,将Segment缓冲区复制到返回值中,并在方法返回之前将其清零.

换句话说:密码没有任何额外的副本.只要您按照指示使用它,它就是完全安全的.


小智 5

好吧,我的不好......我看到对getText()的调用时,所有的铃声都开始振铃,但没有注意到它实际上是我用Action侦听器引入的,这里是一个堆栈跟踪

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
Run Code Online (Sandbox Code Playgroud)

这是使用的代码:

 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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

这是控制台输出:

Awhooa: secret
secret
Run Code Online (Sandbox Code Playgroud)

对于getPassword()的实际调用,也许我错过了什么,但是Segment的缓冲区在哪里归零?我看到一个数组副本,但不是归零.返回的数组将由我自己归零,但Segment的数组仍在那里......

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

并输出:

Before zeroing: [a, b, c] [a, b, c]
After zeroing: [?, ?, ?] [a, b, c]
Run Code Online (Sandbox Code Playgroud)

(我在那里放了问号,因为我无法通过不可打印的字符)

-M