黑客是如何利用所谓的“进程内存转储”偷密码的?

2017-06-24 18:16:45 +08:00
 guyeuro

网上说密码要用 char[]而不是 String 储存的原因是:

虽然 String 加载密码之后可以把这个变量扔掉,但是字符串并不会马上被 GC 回收,一但进程在 GC 执行到这个字符串之前被 dump,dump 出的的转储中就会含有这个明文的字符串。

这个黑客是如何利用所谓的“进程内存转储”偷密码啊? 感觉不可思议啊

3051 次点击
所在节点    问与答
27 条回复
nevin47
2017-06-24 19:02:43 +08:00
看这意思是趁 GC 之前构造一个场景把进程 core 掉,然后 dump 文件里面去找明文密码?不过感觉没啥软用啊,难道是去套软件内私钥的?这时代谁不把私钥先加个密啊
wevsty
2017-06-24 19:45:04 +08:00
string 的本质是帮你在堆上创建了空间储存字符串的,string 自己析构的时候是把空间还给了内存分配器,但是可能没有对空间填充内容(这取决于 string 的实现),并且内存分配器也可能没有对这些空间做任何操作,所以析构以后 dump 内存仍然有可能找到密码。
你用 malloc 分配或者在栈上的数组也会有一样的问题这取决于有关的实现本身。
关键问题不在于你用的到底是什么,而是使用完成后对于需要保密的数据应该手动覆盖填充有关的空间。
ech0x
2017-06-24 20:04:19 +08:00
@wevsty 请教一下,java char[] 的实现不是直接分配一块固定的内存的吗?如果是的话,用 char[] 不是照样可以被获取嘛。
geelaw
2017-06-24 20:09:23 +08:00
实际上可以直接用 Process Explorer 查看进程里面长得像字符串的东西,这也是可能看到密码的。


@ech0x 但是 char[] 是可变的,因此你可以用完之后手动填充为空白。这样做是为了保证用完之后内存里不再有这个串。

然而很糟糕的是,对于复制 GC 这一招还是会被攻击。


@nevin47 如果没有解密私钥的密码怎么解密私钥……?而且要用的时候仍然需要把私钥解密。
ech0x
2017-06-24 20:15:14 +08:00
@geelaw 谢谢了解了。 这么看来这个攻击方式几乎是无解了的嘛。对方只要能获取你的进程基本上就可以被攻击。如果像 c 用指针数组,密码被分散在内存的各处,能不能防范这个攻击呢?
geelaw
2017-06-24 20:18:12 +08:00
@ech0x 不能,只要你的程序能做的,能控制你的程序的程序都能做。
nevin47
2017-06-24 20:21:42 +08:00
@geelaw 私钥可以拆分后再做二次加密,虽然理论可以继续追着去破译,但是这种破译成本已经成指数扩大了
geelaw
2017-06-24 20:31:15 +08:00
@nevin47 你的想法有很多值得二次思考的地方……比如为什么再次加密后破解难度指数增加?用于二次加密的密码存放在哪里?如果也在进程内存,只要读出二次加密的密码再解密私钥密文不就行了?
gamexg
2017-06-24 20:32:06 +08:00
@nevin47 #7 除非自己实现加密库,不然传递给加密库时必须时解密后的,一样存在被拦截的可能。
wevsty
2017-06-24 20:57:51 +08:00
@ech0x
推荐使用 char[]我觉得是一种偏见。
大概会有人认为使用 char[]的话在不再需要密钥之后手动填充这段内存空间会比较方便,覆盖了原本密钥的内容那么就不用担心在这个时刻之后会被人内存 dump 了。
但是实际上,string 和 char[]没有什么不同的地方,可以在析构之前手动填充一下每个元素,甚至可以继承一个自己的 string 然后在析构函数里面强制填充。
geelaw
2017-06-24 21:09:53 +08:00
@wevsty String 是不可变,且 String 是 final,因此这条路走不通。

而且一个程序不可以依赖 finaliser 来做清理,因为在内存足够的硬件上,GC 可以选择永远不回收内存。
billlee
2017-06-24 21:17:59 +08:00
这和 coredump 有什么关系,如果黑客能读取到 coredump, 也就能直接读你的进程内存,或者给你的进程上调试器。
binux
2017-06-24 21:21:18 +08:00
如果黑客能接触到你的内存,他干点别的什么不好?
wevsty
2017-06-24 21:26:44 +08:00
@geelaw
您说得是 JAVA 么?这方面我不太清楚。
从 C++的角度上,string 是可以被继承的,当一个对象的生命周期结束的时候,析构函数必然会被调用。当然重点不在这里,而在于不再需要密钥的时候密钥是否被正确销毁,不管会不会回收内存,只要用完了正确的覆盖掉原始数据就可以了。
owt5008137
2017-06-24 21:30:33 +08:00
啥语言? c 艹的话 char[]被删除了也不一定回立刻被回收
sinxccc
2017-06-24 21:33:26 +08:00
从安全的角度,能被对方物理接触到和远程登录进去的系统是毫无安全性可言的,一旦到那个地步,唯一能做的就是自毁关键数据和销毁相关的密钥,证书等等。在这个情况下还在比较 char [] 和 string 的安全性就好像关心一个重病垂死的人吃东西太少会不会对胃不好一样。

但总有一些例外比如之前的 heartbleed 漏洞,还有一个常见的情况就是从机器上传递出来的 coredump 文件,这些可能会带出一部分内存的情况。针对这种一般是用加密一部分堆的方式来解决,愿意了解细节的话可以搜搜 Akamai secure heap 和 heartbleed 的故事,很是一波三折。
gamexg
2017-06-24 21:44:57 +08:00
看到后面才知道说的时 java,已开始以为时 c,c 无所谓,强制转换然后置 0 即可。
java 不熟悉,看起来没有 SecureString,如果考虑到这个的话的确需要用 char[] 。
geelaw
2017-06-24 21:50:10 +08:00
@wevsty 最开始有 GC,所以假定不是自己管理内存 - - 如果是 C/C++ 那就没这个问题了,毕竟可以自己控制。
guyeuro
2017-06-24 21:58:31 +08:00
@sinxccc

对啊
所以我问的就是,密码要用 char[]而不是 String 储存有啥意义?黑客能接触到内存还去内存上找密码明文?
lrxiao
2017-06-24 22:06:55 +08:00
看 SERT C++指导有一条注意跨越可信边界时候注意 padding bits,感觉和这个案例差不多

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/370806

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX