Java 为什么 JPasswordField.getPassword() 创建一个包含密码的字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/983964/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does JPasswordField.getPassword() create a String with the password in it?
提问by
Swing's JPasswordField
has the getPassword()
method that returns a char array. My understanding of this is that the array can be zeroed immediately after use so that you do not have sensitive things hanging around in memory for long. The old way to retrieve the password was to use getText()
, which returns a String object, but it has been deprecated.
SwingJPasswordField
具有getPassword()
返回字符数组的方法。我对此的理解是数组可以在使用后立即归零,这样您就不会在内存中长时间挂起敏感的东西。检索密码的旧方法是使用getText()
,它返回一个 String 对象,但它已被弃用。
So, my question is why it is actually being used by Java during the retrieval process using getPassword()
??? To be clearer, I was debugging my test app for something else**, I followed the calls and bang... getText()
in JPasswordField
was called and, of course, a nice String object with my password has been created and now is hanging around the memory.
所以,我的问题是为什么它在检索过程中实际上被 Java 使用getPassword()
??? 更清楚地说,我正在为其他东西调试我的测试应用程序**,我按照调用和 bang... getText()
inJPasswordField
被调用,当然,已经创建了一个带有我的密码的不错的 String 对象,现在它挂在内存中.
Try it for yourself:
自己试试:
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();
}
}
Follow up question: is this 'hidden' use of getText()
dangerous in any way? Of course a dedicated attacker WILL get your password if it has compromised the system, I am talking about a less dedicated one ;)
后续问题:这种“隐藏”使用是否有getText()
任何危险?当然,如果攻击者破坏了系统,那么专门的攻击者将获得您的密码,我说的是一个不那么专门的攻击者 ;)
**I came across this while I was looking for a way to actually display some sensitive data on a Swing component without using a String
object. Apparently there is no way to do it unless I am willing to rewrite part (all?) of the Swing API.. not gonna happen.
**我在寻找一种在不使用String
对象的情况下在 Swing 组件上实际显示一些敏感数据的方法时遇到了这个问题。显然,除非我愿意重写 Swing API 的一部分(全部?),否则没有办法做到这一点......不会发生。
回答by Michael Myers
Actually, here's the Sun implementation of getPassword()
:
实际上,这是 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;
}
The only getText
in there is a call to getText(int offset, int length, Segment txt)
, which calls getChars(int where, int len, Segment txt)
, which in turn copies characters directly into the Segment
's buffer. There are no Strings
being created there.
唯一getText
存在的是对 的调用getText(int offset, int length, Segment txt)
,它调用getChars(int where, int len, Segment txt)
,然后将字符直接复制到Segment
的缓冲区中。那里没有Strings
被创造。
Then, the Segment
's buffer is copied into the return value and zeroed out before the method returns.
然后,Segment
的缓冲区被复制到返回值中并在方法返回之前清零。
In other words: There is no extra copy of the password hanging around anywhere. It's perfectly safe as long as you use it as directed.
换句话说:任何地方都没有额外的密码副本。只要您按照指示使用它,它就绝对安全。
回答by Tom Hawtin - tackline
The Swing implementation is too complex to check by hand. You want tests.
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);
}};
}
});
}
}
Looks like the command string for the (pointless) action event to me. There will be other way to cause the effect as well.
对我来说,看起来像是(毫无意义的)动作事件的命令字符串。也会有其他方式来引起效果。
A vaguely modern VM will move objects in memory anyway, so clearing the char[]
does not necessarily work.
一个模糊的现代虚拟机无论如何都会在内存中移动对象,因此清除char[]
它不一定有效。
回答by Tom Hawtin - tackline
Ok, my bad... All the bells started ringing as soon as I saw the call to getText() without noticing that it was actually introduced by me with the Action listener here's a stacktrace
好吧,我的坏...当我看到对 getText() 的调用时,所有的铃声都开始响起,而没有注意到它实际上是我通过 Action 侦听器引入的,这是一个堆栈跟踪
PasswordTest.getText() line: 14
PasswordTest(JTextField).fireActionPerformed() line: not available
PasswordTest(JTextField).postActionEvent() line: not available
JTextField$NotifyAction.actionPerformed(ActionEvent) line: not available
SwingUtilities.notifyAction(Action, KeyStroke, KeyEvent, Object, int) line: not available
Here is the code used:
这是使用的代码:
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();
}
}
And here is the console output:
这是控制台输出:
Awhooa: secret
secret
And for the actual call to getPassword(), maybe I am missing something, but where is Segment's buffer zeroed? I see an array copy, but not a zeroing. The returned array will be zeroed by myself, but Segment's array is still there...
对于对 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, 'Before zeroing: [a, b, c] [a, b, c]
After zeroing: [?, ?, ?] [a, b, c]
');
System.out.println("After zeroing: " + Arrays.toString(a) + " " + Arrays.toString(b));
}
}
And the output:
和输出:
String passText = new String(passField.getPassword());
(I put question marks there because I cannot past unprintable characters)
(我在那里打了问号,因为我不能超过不可打印的字符)
-M
-M
回答by Draemon
**I came across this while I was looking for a way to actually display some sensitive data on a Swing component without using a String object. Apparently there is no way to do it unless I am willing to rewrite part (all?) of the Swing API.. not gonna happen.
**我在寻找一种在不使用 String 对象的情况下在 Swing 组件上实际显示一些敏感数据的方法时遇到了这个问题。显然,除非我愿意重写 Swing API 的一部分(全部?),否则没有办法做到这一点......不会发生。
You can tell a JPasswordField
to display the characters by calling field.setEchoChar('\0')
. This retains the rest of the protection offered by JPasswordField
(no String
s, no cut/copy).
您可以JPasswordField
通过调用来告诉 a显示字符field.setEchoChar('\0')
。这保留了JPasswordField
(no String
s, no cut/copy)提供的其余保护。
回答by Ali Ben Messaoud
This works for me and helps you to build a Stringified password:
这对我有用,可以帮助您构建字符串化密码:
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
}
回答by Jayson
String.valueOf(txtPass.getPassword())
回答by Riduan S
This works for me.
这对我有用。
##代码##