字符数组隐藏密码 jdbc
array of characters hide password jdbc
我正尝试在我的 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);
首先,让我们弄清楚为什么 char[]
比 String
更适合密码。
如果您假设黑客可以转储 JVM 的内存映像,或以任何其他方式搜索您的内存 space,那么只要有足够的时间,他们就可以找到内存中的密码。出于这个原因,最好将密码在内存中保留很短的时间 - 从用户那里获取密码,将其传递到任何需要它的地方,然后将其从内存中删除。
天真地,人们会做这样的事情:
String password = getPasswordFromUser();
usePassword(password);
password = null;
现在密码字符串(假设它是 "myPass"
)已准备好进入垃圾收集。但它仍然在记忆中。人们永远不知道什么时候会收集到它。也许永远不会,因为您有足够的内存供您的应用程序使用。即使收集了,也不能保证记忆会被抹去。黑客需要很长时间才能彻底搜索您的内存 space 以查找可能的密码。
现在,您可能听说过字符数组是一个很好的解决方案。因此,您将上面的原始代码替换为:
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];
实际上是违反了那个规则。它在 sPassword
中创建了一个 String
对象,其中包含密码,我们失去了安全性。
这里真正的问题是连接到 JDBC 不允许您传递 char[]
的密码。 DriverManager.getConnection()
方法需要 String
,而不是 char[]
,无论您是将密码放在连接 URL 中,还是将其作为参数传递。
所以事实上,如果你想为通过 JDBC 的连接保持这种安全级别,你将必须找到一种不使用 [=29] 的替代方法来验证你的数据库=] 或 DriverManager.getConnection(String)
.
我没有测试它,但 MySQL 提供了一种使用 SSL 和客户端证书进行身份验证的方法。 Connector/J 可能还有其他使用其他身份验证插件的方法。 但是要求用户输入用户名和密码并使用 DriverManager.getConnection()
的简单方法不会让您通过 char[]
.[=33 维护密码安全=]
我正尝试在我的 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);
首先,让我们弄清楚为什么 char[]
比 String
更适合密码。
如果您假设黑客可以转储 JVM 的内存映像,或以任何其他方式搜索您的内存 space,那么只要有足够的时间,他们就可以找到内存中的密码。出于这个原因,最好将密码在内存中保留很短的时间 - 从用户那里获取密码,将其传递到任何需要它的地方,然后将其从内存中删除。
天真地,人们会做这样的事情:
String password = getPasswordFromUser();
usePassword(password);
password = null;
现在密码字符串(假设它是 "myPass"
)已准备好进入垃圾收集。但它仍然在记忆中。人们永远不知道什么时候会收集到它。也许永远不会,因为您有足够的内存供您的应用程序使用。即使收集了,也不能保证记忆会被抹去。黑客需要很长时间才能彻底搜索您的内存 space 以查找可能的密码。
现在,您可能听说过字符数组是一个很好的解决方案。因此,您将上面的原始代码替换为:
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];
实际上是违反了那个规则。它在 sPassword
中创建了一个 String
对象,其中包含密码,我们失去了安全性。
这里真正的问题是连接到 JDBC 不允许您传递 char[]
的密码。 DriverManager.getConnection()
方法需要 String
,而不是 char[]
,无论您是将密码放在连接 URL 中,还是将其作为参数传递。
所以事实上,如果你想为通过 JDBC 的连接保持这种安全级别,你将必须找到一种不使用 [=29] 的替代方法来验证你的数据库=] 或 DriverManager.getConnection(String)
.
我没有测试它,但 MySQL 提供了一种使用 SSL 和客户端证书进行身份验证的方法。 Connector/J 可能还有其他使用其他身份验证插件的方法。 但是要求用户输入用户名和密码并使用 DriverManager.getConnection()
的简单方法不会让您通过 char[]
.[=33 维护密码安全=]