在Swing中,密码字段具有getPassword() (返回char[] )方法,而不是通常的getText() (返回String )方法。 同样,我遇到了不使用String处理密码的建议。

为什么在密码方面String会对安全构成威胁? 使用char[]感觉很不方便。


#1楼

这些都是原因,应该选择一个char []数组而不是String作为密码。

1.由于字符串在Java中是不可变的,因此,如果您将密码存储为纯文本格式,它将在内存中可用,直到垃圾回收器将其清除为止,并且由于字符串在字符串池中用于可重用性,因此很有可能长时间保留在内存中,构成安全威胁。

由于有权访问内存转储的任何人都可以以明文形式找到密码,这是另一个原因,您应该始终使用加密密码而不是纯文本。 由于字符串是不可变的,因此无法更改字符串的内容,因为任何更改都会产生一个新的字符串,而如果使用char [],仍可以将所有元素设置为空白或零。 因此,将密码存储在字符数组中可以明显减轻窃取密码的安全风险。

2. Java本身建议使用JPasswordField的getPassword()方法,该方法返回char [],而不建议使用不赞成使用的getText()方法,该方法以明文形式返回密码,以说明安全性原因。 遵循Java团队的建议并遵守标准而不是违背标准是很好的。

3.使用String总是存在在日志文件或控制台中打印纯文本的风险,但是如果您使用Array,则不会打印数组的内容,而是会打印其内存位置。 尽管这不是真正的原因,但仍然有意义。

String strPassword="Unknown";
char[] charPassword= new char[]{'U','n','k','w','o','n'};
System.out.println("String password: " + strPassword);
System.out.println("Character password: " + charPassword);String password: Unknown
Character password: [C@110b053

从此博客引用。 我希望这有帮助。


#2楼

除非您在使用后手动清理它,否则char数组不会给您vs String带来什么好处,而且我还没有看到有人真正这样做过。 所以对我来说,char [] vs String的偏爱有点夸张。

看看这里 广泛使用的 Spring Security库,问问自己-Spring Security家伙不称职或char []密码没有多大意义。 当一些讨厌的黑客抓取您RAM的内存转储时,请确保即使您使用复杂的方法将其隐藏,也可以获取所有密码。

但是,Java一直在变化,某些令人恐惧的功能(例如Java 8的String Deduplication功能)可能会在您不知情的情况下插入String对象。 但这是另一回事。


#3楼

编辑:经过一年的安全研究,回到这个答案,我意识到,这很不幸地暗示您实际上会比较纯文本密码。 请不要 使用带有盐和合理迭代次数的安全单向哈希 。 考虑使用图书馆:这东西很难弄对!

原始答案: String.equals()使用短路评估 ,因此容易受到定时攻击的事实呢? 可能不太可能,但是理论上您可以安排密码比较的时间,以便确定正确的字符顺序。

public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;// Quits here if Strings are different lengths.if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;// Quits here at first different character.while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;
}

有关计时攻击的更多资源:

  • 定时攻击的教训
  • 有关定时攻击信息安全堆栈交换的讨论
  • 当然, 定时攻击维基百科页面

#4楼

正如乔恩·斯基特(Jon Skeet)所言,除了使用反射之外,别无他法。

但是,如果您可以选择反射,则可以执行此操作。

public static void main(String[] args) {System.out.println("please enter a password");// don't actually do this, this is an example only.Scanner in = new Scanner(System.in);String password = in.nextLine();usePassword(password);clearString(password);System.out.println("password: '" + password + "'");
}private static void usePassword(String password) {}private static void clearString(String password) {try {Field value = String.class.getDeclaredField("value");value.setAccessible(true);char[] chars = (char[]) value.get(password);Arrays.fill(chars, '*');} catch (Exception e) {throw new AssertionError(e);}
}

运行时

please enter a password
hello world
password: '***********'

注意:如果字符串的char []已作为GC周期的一部分复制,则前一个副本有可能在内存中的某个位置。

这个旧副本不会出现在堆转储中,但是如果您可以直接访问该进程的原始内存,则可以看到它。 通常,您应该避免任何人具有这种​​访问权限。


#5楼

字符串是不可变的,一旦创建就不能更改。 将密码创建为字符串将在堆或字符串池上留下对该密码的杂散引用。 现在,如果有人对Java进程进行了堆转储并仔细扫描了一下,他也许可以猜出密码。 当然,这些未使用的字符串将被垃圾回收,但这取决于GC启动的时间。

另一方面,一旦完成身份验证,就可以更改char [],您可以使用任何字符(如所有M或反斜杠)覆盖它们。 现在,即使有人进行堆转储,他也可能无法获得当前未使用的密码。 从某种意义上来说,这给了您更多的控制权,例如您自己清除对象内容与等待GC完成操作。


#6楼

简短而直接的答案是因为char[]是可变的,而String对象不是。

Java中的Strings是不可变的对象。 这就是为什么一旦创建便无法对其进行修改,因此从内存中删除其内容的唯一方法是对其进行垃圾回收。 只有当对象释放的内存可以被覆盖并且数据将消失时,才会这样。

现在,Java中的垃圾回收不会在任何保证的时间间隔内发生。 因此, String可以在内存中保留很长时间,并且如果在这段时间内进程崩溃,则字符串的内容可能最终出现在内存转储或某些日志中。

使用字符数组 ,您可以读取密码,尽快完成密码的使用,然后立即更改内容。


#7楼

Java中的字符串是不可变的。 因此,无论何时创建字符串,它都会保留在内存中,直到被垃圾回收为止。 因此,任何有权访问内存的人都可以读取字符串的值。
如果字符串的值被修改,那么它将最终创建一个新的字符串。 因此,原始值和修改后的值都会保留在内存中,直到被垃圾回收为止。

使用字符数组,一旦达到密码的目的,就可以修改或删除数组的内容。 修改后,甚至在垃圾回收开始之前,都无法在内存中找到数组的原始内容。

出于安全考虑,最好将密码存储为字符数组。


#8楼

字符串是不可变的,它进入字符串池。 一旦写入,就不能覆盖。

char[]是一个数组,使用密码后应该覆盖它,这是应该这样做的方式:

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {allowUser = true;cleanPassword(passw);cleanPassword(dbPassword);passw=null;
}private static void cleanPassword (char[] pass) {for (char ch: pass) {ch = '0';}
}

攻击者可以使用它的一种情况是崩溃转储-当JVM崩溃并生成内存转储时-您将能够看到密码。

那不一定是恶意的外部攻击者。 这可能是支持用户,可以访问服务器以进行监视。 他可以窥视一个崩溃转储并找到密码。


#9楼

是否为此目的应使用String还是使用Char []尚有争议,因为两者都有其优点和缺点。 这取决于用户的需求。

由于Java中的字符串是不可变的,因此每当有人尝试操纵您的字符串时,它都会创建一个新的Object,而现有的String不会受到影响。 这可能被视为将密码存储为字符串的一个优点,但是即使使用该对象,该对象仍保留在内存中。 因此,如果有人以某种方式获得了对象的存储位置,则该人可以轻松地跟踪存储在该位置的密码。

Char []是可变的,但是它的优点是程序员使用它后可以显式清理数组或覆盖值。 因此,使用完毕后,它就会被清除,而且没人能知道您所存储的信息。

根据以上情况,可以根据需要选择使用String还是使用Char []。


#10楼

字符串是不可变的 。 这意味着一旦创建了String ,如果另一个进程可以转储内存,就无法(除了Reflection )在垃圾回收开始之前摆脱数据。

使用数组,您可以在使用完数据后显式擦除数据。 您可以用任何您喜欢的东西覆盖阵列,并且即使在垃圾回收之前,密码也不会出现在系统中的任何位置。

因此,是的,这一个安全性问题-但是即使使用char[]只会减少攻击者的机会之窗,并且仅针对这种特定类型的攻击。

如评论中所述,垃圾回收器移动的数组可能会将数据的零散副本保留在内存中。 我相信这是特定于实现的-垃圾收集器可能会清除所有内存,以免发生这种情况。 即使这样做,在char[]仍然包含实际字符作为攻击窗口的时间内仍然存在。


#11楼

使用后,可以通过将每个字符设置为零而不能将字符串设置为零来清除字符数组( char[] )。 如果有人能够以某种方式看到内存映像,则在使用字符串的情况下,他们可以看到纯文本密码,但是如果使用char[] ,则在将数据清除为0后,密码是安全的。


#12楼

我认为这不是一个有效的建议,但是,我至少可以猜测原因。

我认为这样做的动机是要确保您可以在使用后立即确定地擦除内存中所有的密码痕迹。 使用char[]可以确定用空格或其他东西覆盖数组的每个元素。 您不能以这种方式编辑String的内部值。

但这并不是一个好的答案。 为什么不仅仅确保对char[]String的引用不会转义? 这样就没有安全问题了。 但事实是, String对象可以在理论上进行intern()编辑,并在常量池中保持活动状态。 我想使用char[]禁止这种可能性。


#13楼

有人认为,一旦不再需要密码,您就必须覆盖用于存储密码的内存。 这减少了攻击者从系统读取密码的时间窗口,并且完全忽略了攻击者已经需要足够的访问权来劫持JVM内存的事实。 具有这么多访问权限的攻击者可以捕获您的关键事件,从而使这完全无用(AFAIK,如果我错了,请纠正我)。

更新资料

感谢评论,我必须更新我的答案。 显然,在两种情况下,这可以增加(非常)小的安全性改进,因为它减少了密码在硬盘驱动器上的停留时间。 我仍然认为,对于大多数用例而言,这是过大的选择。

  • 您的目标系统可能配置错误,或者您必须假设目标系统配置错误,并且必须对核心转储抱有幻想(如果系统不是由管理员管理的,则可能是有效的)。
  • 您的软件必须过于偏执,以防止攻击者获得对硬件的访问权限,从而防止数据泄露-使用TrueCrypt (已停产), VeraCrypt或CipherShed之类的东西 。

如果可能,禁用核心转储和交换文件将解决这两个问题。 但是,它们将需要管理员权限,并且可能会减少功能(减少使用的内存),并且从运行中的系统中提取RAM仍然是一个有效的问题。


#14楼

尽管这里的其他建议似乎是有效的,但还有另一个很好的理由。 使用纯String您更有可能不小心将密码打印到日志 ,监视器或其他不安全的地方。 char[]不太容易受到攻击。

考虑一下:

public static void main(String[] args) {Object pw = "Password";System.out.println("String: " + pw);pw = "Password".toCharArray();System.out.println("Array: " + pw);
}

印刷品:

String: Password
Array: [C@5829428e

#15楼

为了引用官方文档,《 Java密码学体系结构指南》说这是关于char[]String密码(关于基于密码的加密,但这当然更普遍地是关于密码):

收集密码并将其存储在java.lang.String类型的对象中似乎合乎逻辑。 但是,有一个警告: String类型的Object是不可变的,即,没有定义的方法允许您在使用后更改(覆盖)或将String的内容归零。 此功能使String对象不适合存储对安全敏感的信息,例如用户密码。 您应该始终将安全敏感信息收集并存储在char数组中。

Java编程语言版本4.0的安全编码准则的准则2-2也表示类似的内容(尽管它最初是在日志记录的上下文中):

准则2-2:请勿记录高度敏感的信息

某些信息(例如,社会安全号码(SSN)和密码)非常敏感。 该信息的保存时间不应超过必要的时间,也不能保存在任何地方,即使是管理员也是如此。 例如,不应将其发送到日志文件,并且不应通过搜索来检测其存在。 某些瞬态数据可以保存在可变数据结构中,例如char数组,并在使用后立即清除。 清除数据结构在典型的Java运行时系统上降低了效率,因为对象在内存中对程序员透明地移动。

该指南还对不具有所处理数据语义知识的低层库的实现和使用产生影响。 例如,低级字符串解析库可能会记录其工作的文本。 应用程序可以使用库解析SSN。 这会导致管理员可以访问日志文件使用SSN。


#16楼

答案已经给出,但我想分享一下我最近在Java标准库中发现的一个问题。 尽管他们现在非常注意在所有地方都用char[]替换密码字符串(这当然是一件好事),但在从内存中清除它时,其他对安全性要求较高的数据却似乎被忽略了。

我在想例如PrivateKey类。 考虑一种方案,您将从PKCS#12文件中加载私有RSA密钥,并使用它执行一些操作。 现在,在这种情况下,只要适当限制对密钥文件的物理访问,仅嗅探密码将无济于事。 作为攻击者,如果直接获得密钥而不是密码,那么情况会更好。 所需信息可能会泄漏,核心转储,调试器会话或交换文件只是一些示例。

事实证明,没有什么可以让您从内存中清除PrivateKey的私有信息的,因为没有API可以让您擦除构成相应信息的字节。

这是一种糟糕的情况,因为本文描述了如何潜在地利用这种情况。

例如,OpenSSL库在释放私钥之前会覆盖关键内存部分。 由于Java是垃圾回收的,因此我们需要显式的方法来擦除Java密钥的私有信息并使这些信息无效,这些信息将在使用密钥后立即应用。

为什么密码比字符串更喜欢char []?相关推荐

  1. java为什么密码比字符串更喜欢char []?

    在Swing中,密码字段具有getPassword()(returns char[])方法,而不是通常的getText()(returns String)方法.同样,我遇到了不使用String密码的建 ...

  2. LeetCode 1869. 哪种连续子字符串更长

    文章目录 1. 题目 2. 解题 1. 题目 给你一个二进制字符串 s .如果字符串中由 1 组成的 最长 连续子字符串 严格长于 由 0 组成的 最长 连续子字符串,返回 true :否则,返回 f ...

  3. 六种流行的语言大餐---C、C++、python、Java、php、C#你更喜欢哪一个呢?

    引言 鉴于五一期间超大的人流量,LZ思来想去,最终还是选择蜗居在自己的出租屋.无聊之际,当然不能忘了做点什么事情,于是LZ就研究了一下几种语言的皮毛,在这里献丑一翻,希望各位猿友莫要见笑. 不过说来也 ...

  4. 纸上原型设计 VS 桌面原型工具设计,你更喜欢谁?

    2019独角兽企业重金招聘Python工程师标准>>> 纸上原型设计,作为传统的原型设计方式,简单快速,成本低廉,为大部分设计师所喜爱.而桌面原型工具设计,作为伴随电脑科技发展而出现 ...

  5. AI一分钟 | 刚爆Python将进入高考,AI就已强势进入公务员国考试卷;报告称1/4男性更喜欢跟机器人谈恋爱,真的吗?

    一分钟AI 人工智能进入国考试卷 上百万人参加2018年度国考笔试,我们距离AI还有多远? 发改委正在牵头制定智能汽车创新发展战略,2级水平以上的自动驾驶在2020年要达到10%左右 未来你会跟机器人 ...

  6. 南京、苏州、杭州、上海你更喜欢哪座城市?

    南京.苏州.杭州.上海你更喜欢哪座城市? 这个问题笔者来回答,实在是很合适.笔者在这四个城市里工作过,以出差做SAP咨询项目的方式,在这些城市里至少生活过半年.在这些城市生活期间笔者游历了这些城市的各 ...

  7. 我为什么更喜欢 Mac OS X

    我为什么更喜欢 Mac OS X 自从苹果的iPhone和iPad在市场上取得傲人的成绩之后,国内的用户对于苹果的MacBook笔记本以及iMac机器的热情也越来越高.但是很多人是买了Apple的电脑 ...

  8. (转)javascript 杂谈之哪种写法你更喜欢?

    原文地址:http://www.cnblogs.com/baochuan/archive/2012/04/30/2473771.html 思维导图 介绍 老是在写js,你平时是怎么写你的js呢?更喜欢 ...

  9. 现在使用控件, 更喜欢继承(覆盖控件已有的函数,很奇怪的一种使用方式)...

    以前写代码, 总是把主单元弄得满满当当; 现在更喜欢把控件比较独立的功能写成一个单元, 改写属性.重载方法...哪怕只有一点点和默认不同, 也喜欢独立出来. 刚刚用到 TListBox, 需要能拖动元 ...

最新文章

  1. 计算机系统的基本功能,计算机系统的主要功能是什么
  2. linux automake 卸载,linux下open-vswitch安装卸载操作
  3. 操作符*或者-的重载
  4. 4.22、Bootstrap V4自学之路-----内容---轮播
  5. Monkey测试com.android.browser 发 生类似android.datab
  6. Linux命令之pstree - 以树状图显示进程间的关系
  7. 非线编辑软件 linux,Flowblade 2.0 发布,非线性开源Linux视频编辑器
  8. Spring实战(六)自动装配的歧义性
  9. css3 呼吸的莲花_CSS3制作莲花盛开动画
  10. 【华为云技术分享】AI 开发路漫漫,什么才是真正的极客精神?
  11. 打印工资条怎么做到每个人都有表头明细_现在还在用订书钉装订工资条,算是一种落后吗?你的公司怎么发?...
  12. Oracle的order by关键字
  13. codevs 1415 比那名居天子
  14. 【服务器】【个人网盘】宝塔搭建cloudreve
  15. 影响程序运行速度的几个因素
  16. Selenium Automated test 's Installation environment
  17. bootstrap-table分页插件使用
  18. java .jar下载_java jar包全集
  19. java 打开 覆盖文件_如何用JAVA实现文件的覆盖
  20. python requests返回结果带\\u的解决方法

热门文章

  1. 算法-二叉树中的最大路径和
  2. 算法--------字母异位词分组 (Java 版本)
  3. Ubuntu adb 报错:no permissions (user in plugdev group; are your udev rules wrong?);
  4. Android 停止调试程序
  5. Android stadio
  6. php 将内容中的图片的域名,php给编辑器中的图片地址添加域名
  7. LeetCode 75. 颜色分类(Sort Colors)
  8. C# 手动读写app config 的源码
  9. 基于StringTemplate的视图
  10. 解决html连续字符或数字换行的问题