Java安全性:如何清除/清除与对象关联的内存? (和/或确保这是特定变量的唯一实例/副本) [英] Java security: how to clear/zero-out memory associated with an object? (And/or ensure that is the only instance/copy of a particular variable)

查看:169
本文介绍了Java安全性:如何清除/清除与对象关联的内存? (和/或确保这是特定变量的唯一实例/副本)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在讨论如何保护存储在Java程序中的敏感信息(例如密码)。根据安全要求,清除包含敏感信息的存储器,例如,通过将字节值设置为全零。担心的是攻击者可以观察与应用程序进程相关联的内存,因此我们希望尽可能地限制此类敏感信息的时间窗口。以前,项目涉及C ++,所以memset()就足够了。



(顺便提一下,memset()的使用受到质疑,因为已知某些编译器会对其进行优化。基于以下假设使用得到的二进制文件,因为以后没有使用内存,所以首先不需要将它归零。对于那些谷歌用于memset和clear memory的人来说,这个模糊不清等等。



现在我们手中有一个Java项目被迫按此要求。



For Java对象,我的理解是:




  • 一个空的引用只会改变引用的值;对象的堆上的内存仍然包含数据

  • 像String这样的不可变对象无法修改它的数据(或至少不容易,在VM的范围内)一个适当启用的安全管理器)

  • 世代垃圾收集器可以在整个地方制作对象的副本(如上所述这里



对于原语,我的理解是:




  • 本地方法中的原始类型变量将在堆栈上分配,和:

  • 当你更改它的值时,你可以直接在内存中修改它(而不是使用引用来处理堆上的对象)。

  • 在某些情况下,可以/将在幕后制作副本,例如将其作为参数传递给方法或装箱(自动或不装)创建包含的包装器的实例另一个保持相同值的原始变量。



我的同事声称Java原语是不可变的,并且有来自NSA和Oracle的文档关于Java对此要求缺乏支持。



我的立场是,通过将值设置为零(或布尔值),原语可以(至少在某些情况下)归零假,并且内存以这种方式被清除。



我正在尝试验证JLS或其他官方文档中是否有关于所需行为的语言JVM涉及基元的内存管理。我能找到的最接近的是Java编程语言的安全编码指南在甲骨文网站上提到使用后清除字符数组。



当我的同事称为基元不可变时,我会对定义狡辩,但我很确定他的意思记忆不能被恰当地归零 - 让我们不要担心。我们没有讨论他是否意味着最终变量 - 从我们正在谈论的背景来看。



对此有没有明确的答案或参考?我很感激任何可以告诉我哪里错了或确认我是对的。



编辑:经过进一步讨论后,我已经能够澄清我的同事正在考虑原始包装,而不是原始本身。因此,我们留下了如何安全地清除内存的原始问题,最好是对象。此外,澄清一下,敏感信息不仅仅是密码,还包括IP地址或加密密钥等。



是否有任何商业JVM提供优先处理等功能某些物品? (我想这实际上会违反Java规范,但我想我会问以防万一我错了。)

解决方案

<编辑:实际上我只有三个可能确实有用的想法 - 至少对于工作的不同值。



第一个或多或少记录的是ByteBuffer.allocateDirect!据我所知,allocateDirect在通常的Java堆之外分配缓冲区,因此不会被复制。我找不到任何关于它不会在所有情况下被复制的硬保证 - 但对于当前的Hotspot VM实际情况(即它在额外的堆中分配)并且我认为这将保持这种方式。



第二个是使用sun.misc.unsafe软件包 - 正如名称所说的那样有一些相当明显的问题,但至少这个问题几乎与使用过的VM无关 - 或者它支持(并且它可以工作)或它不支持(并且你得到链接错误)。问题是,使用这些东西的代码会很快变得非常复杂(单独获得一个不安全的变量是非常重要的)。



第三个是分配比实际需要的更大,更大,以便在旧代堆中分配对象开始:


l -XX:PretenureSizeThreshold =可以设置为限制年轻
代中
分配的大小。任何大于
的分配都不会在
年轻代中尝试,因此将分配给旧代的


我认为THAT解决方案的缺点很明显(默认大小似乎约为64kb)。






无论如何,旧答案:



是的,因为我看到你几乎不能保证存储在堆上的数据被100%删除而不留下副本(如果你不想要一般的解决方案,那就更好了,但是那个可以使用它的人说当前的Hotspot VM及其默认的垃圾收集器)。 p>

正如你的链接帖子所述(这里),垃圾收集器几乎让这无法保证。实际上与帖子所说的问题相反,这里的问题不是代际GC,而是Hotspot VM(现在我们是特定于实现的)正在使用某种Stop&默认情况下将gc复制到年轻代。



这意味着只要在将密码存储在char数组中并将其清零之间发生垃圾收集,就会得到只有在下一次GC发生时才会覆盖的数据副本。请注意,对一个对象的使用将具有完全相同的效果,但不是将其复制到太空而是将其复制到旧一代堆中 - 我们最终会从空间中复制未被覆盖的数据。



为了避免这个问题,我们非常需要某种方法来保证在存储密码和归零之间发生NO垃圾收集或者从get存储char数组进入老一代堆。还要注意,这依赖于Hotspot VM的内部可能会很好地改变(实际上有不同的垃圾收集器可以生成更多的副本; iirc Hotspot VM支持使用列车算法的并发GC)。 幸运的是它是不可能保证其中任何一个(afaik每个方法调用/返回引入一个安全点!),所以你甚至不想尝试(特别是考虑到我没有看到任何方法来确保JIT没有优化归零);)



似乎保证数据只存储在一个位置的唯一方法就是使用JNI来实现它。



PS:请注意,虽然以上仅适用于堆,但您无法保证堆栈的更多内容(JIT可能会优化写操作而无需读取堆栈离开,所以当你从函数返回时,数据仍然在堆栈上)


I'm in a discussion at work over how to secure sensitive information (e.g. passwords) stored in a Java program. Per security requirements, memory containing sensitive information is cleared, e.g. by setting the values of the bytes to all zeroes. The concern is that an attacker can observe the memory associated with the application process, and so we want to limit as much as possible the window of time such sensitive information hangs around. Previously, projects involved C++, so a memset() sufficed.

(Incidentally, the use of memset() has been called into question because some compilers are known to optimize it's use out of the resulting binary based on the assumption that, since the memory is not used later, there is no need to zero it in the first place. This blurb is a disclaimer for those who Google for "memset" and "clear memory", etc).

Now we have on our hands a Java project being pressed against this requirement.

For Java objects, my understanding is that:

  • a nulled reference only changes the value of the reference; the memory on the heap for the object still contains data
  • an immutable object like String would not be able to have it's data modified (or at least not easily, within the confines of a VM with an appropriately enabled security manager)
  • the generational garbage collectors may make copies of objects all over the place (as noted here)

And for primitives, my understanding is that:

  • a primitive-type variable in a local method would get allocated on the stack, and:
  • when you change it's value, you modify it directly in memory (as opposed to using a reference to handle an object on the heap).
  • copies can/would be made "behind the scenes" in some situations, such as passing it as an argument into methods or boxing (auto- or not) creating instances of the wrappers which contain another primitive variable holding the same value.

My coworker claims that Java primitives are immutable and that there is documentation from both the NSA and Oracle regarding the lack of support in Java for this requirement.

My position is that primitives can (at least in some situations) be zeroed by setting the value to zero (or boolean to false), and the memory is cleared that way.

I'm trying to verify if there's language in the JLS or other "official" documentation about the required behavior of JVMs when it comes to memory management with respect to primitives. The closest I could find was a "Secure Coding Guidelines for the Java Programming Language" on Oracle's site which mentions clearing char arrays after use.

I'd quibble over definitions when my coworker called primitives immutable, but I'm pretty sure he meant "memory cannot be appropriately zeroed" - let's not worry about that. We did not discuss whether he meant final variables - from context we were talking in general.

Are there any definitive answers or references on this? I'd appreciate anything that could show me where I'm wrong or confirm that I'm right.

Edit: After further discussion, I've been able to clarify that my coworker was thinking of the primitive wrappers, not the primitives themselves. So we are left with the original problem of how to clear memory securely, preferably of objects. Also, to clarify, the sensitive information is not just passwords, but also things like IP addresses or encryption keys.

Are there any commercial JVMs which offer a feature like priority handling of certain objects? (I imagine this would actually violate the Java spec, but I thought I'd ask just in case I'm wrong.)

解决方案

Edit: Actually I just had three ideas that may indeed work - for different values of "work" at least.

The first that is more or less documented would be ByteBuffer.allocateDirect! As I understand it allocateDirect allocates the buffer outside the usual java heap so won't be copied around. I can't find any hard guarantees about it not getting copied in all situations though - but for the current Hotspot VM that is actually the case (ie it's allocated in an extra heap) and I assume this will stay that way.

The second one is using the sun.misc.unsafe package - which as the name says has some rather obvious problems but at least that would be pretty much independent of the used VM - either it's supported (and it works) or it's not (and you get linking errors). The problem is, that the code to use that stuff will get horribly complicated pretty fast (alone getting an unsafe variable is non trivial).

The third one would be to allocate a much, much, MUCH larger size than is actually needed, so that the object gets allocated in the old generation heap to begin:

l-XX:PretenureSizeThreshold= that can be set to limit the size of allocations in the young generation. Any allocation larger than this will not be attempted in the young generation and so will be allocated out of the old generation.

Well the drawback of THAT solution is obvious I think (default size seems to be about 64kb).

. .

Anyways here the old answer:

Yep as I see it you pretty much cannot guarantee that the data stored on the heap is 100% removed without leaving a copy (that's even true if you don't want a general solution but one that'll work with say the current Hotspot VM and its default garbage collectors).

As said in your linked post (here), the garbage collector pretty much makes this impossible to guarantee. Actually contrary to what the post says the problem here isn't the generational GC, but the fact that the Hotspot VM (and now we're implementation specific) is using some kind of Stop & Copy gc for its young generation per default.

This means that as soon as a garbage collection happens between storing the password in the char array and zeroing it out you'll get a copy of the data that will be overwritten only as soon as the next GC happens. Note that tenuring an object will have exactly the same effect, but instead of copying it to to-space it's copied to the old generation heap - we end up with a copy of the data in from space that isn't overwritten.

To avoid this problem we'd pretty much need some way to guarantee that either NO garbage collection is happening between storing the password and zeroing it OR that the char array is stored from the get go in the old generation heap. Also note that this relies on the internas of the Hotspot VM which may very well change (actually there are different garbage collectors where many more copies can be generated; iirc the Hotspot VM supports a concurrent GC using a train algorithm). "luckily" it's impossible to guarantee either one of those (afaik every method call/return introduces a safe point!), so you don't even get tempted to try (especially considering that I don't see any way to make sure the JIT doesn't optimize the zeroing out away) ;)

Seems like the only way to guarantee that the data is stored only in one location is to use the JNI for it.

PS: Note that while the above is only true for the Heap, you can't guarantee anything more for the stack (the JIT will likely optimize writes without reads to the stack away, so when you return from the function the data will still be on the stack)

这篇关于Java安全性:如何清除/清除与对象关联的内存? (和/或确保这是特定变量的唯一实例/副本)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆