Feature #5741
openSecure Erasure of Passwords
Description
In other languages it is considered good practice to securely erase
passwords immediately after they were used. Imagine authentication
in a web app - ultimately a String containing the password arrives
at the server, where it will be processed and compared to some
previously stored value. After this is done, there is no need to
store these password Strings any longer, so they should be
discarded right away (more on why later).
In C, you would simply overwrite the array of bytes with zeroes or
random values. In Java, Strings are immutable, that's why there it
is common practice to use char[] for all things password and overwrite
them when done.
Currently, there is no way in Ruby to overwrite the memory that
was used by a String. String#clear and String#replace both use
str_discard internally, which only frees the underlying pointer
without overwriting it.
The problem with not erasing passwords is this: the contents of the
String stay in memory until they are finally GC'ed. But even then
only the pointer will be freed, leaving the contents mostly intact
until the memory is reclaimed and overwritten later on.
This could be exploited if an attacker had access to the memory of
the server. This could happen in many ways: a core dump after a
crash, access to the host if the server runs in a VM, or even by
deep-freezing the DRAM :) [1]
It could be argued that given the examples above, much more
devastating attacks would be possible since in all of those
cases you more or less have physical access to the machine. But
I would still consider this to be a valid concern, if not only
for the reason of never opening additional attack surfaces if
they can be avoided relatively easily.
I also found [2], which seems to show that Python deals with
similar problems and it also contains more background info.
Eric Hodel and I discussed this yesterday and Eric came up with
a C extension that can be used to illustrate the problem (attached).
If you inspect the resulting core dump, you will find the following:
- the untouched String remains in memory fully intact
- the String#clear'ed String remains to a large extent, typically the
first character is missing - so if you typed "PASSWORD", search for
"ASSWORD" (unintentional pun) instead - The String#clear_secure'ed will have been completely erased, no
traces remain
My questions:
- Would you agree that we need this functionality?
- Where would we ideally place it? I'm not sure whether
String is the perfect place, but on the other hand, String
is the only place where we have access to the implementation
details. - Are there better alternative ways how we could achieve this?
[1] http://www.schneier.com/blog/archives/2008/02/cold_boot_attac.html
[2] http://stackoverflow.com/questions/728164/securely-erasing-password-in-memory-python
Files
Updated by normalperson (Eric Wong) about 13 years ago
Martin Bosslet Martin.Bosslet@googlemail.com wrote:
- Are there better alternative ways how we could achieve this?
You can use IO#read
/ StringIO#read
to overwrite an existing String:
----------- /tmp/pass.rb -----------------
pass = ""
$stdin.sysread(256, pass) # assuming a line-buffered terminal
io = StringIO.new("\0" * pass.bytesize)
io.read(pass.bytesize, pass)
p pass
Process.kill(:ABRT, $$)
sleep # wait for SIGABRT to hit us
$ ulimit -c unlimited
# using "hunter2" as my password:
$ ruby /tmp/pass.rb
hunter2
"\x00\x00\x00\x00\x00\x00\x00\x00"
Aborted (core dumped)
$ strings core | grep hunter
<nothing>
Unfortunately, using things like $stdin.gets
to read passwords:
pass = $stdin.gets
Instead of:
$stdin.sysread(256, pass)
...Can leave a copy of the password in userspace IO
buffers (stdio or
rb_io_t
). One has to avoid touching userspace IO
buffering layers to
avoid leaving a trace of them in a core dump.
Updated by normalperson (Eric Wong) about 13 years ago
Eric Wong normalperson@yhbt.net wrote:
You can use
IO#read
/StringIO#read
to overwrite an existing String:
String#tr!
(on binary strings) can also work:
# -*- encoding: binary -*-
pass = ""
$stdin.sysread(256, pass)
pass.tr!("\0-\xff", "\0")
p pass
Process.kill(:ABRT, $$)
sleep
Maybe String#gsub!
would, too, but regexp engines are quite complex and
may do buffering/copying of its own (I don't know regexp implementation
details well). tr
/tr!
is pretty simple...
I'd still trust IO#sysread
the most since passing a String
buffer to it
is for optimization. It would be stupid (and thus highly unlikely :)
that any Ruby implementation would copy/free() a buffer passed for IO
and replace it with another buffer internally.
Updated by MartinBosslet (Martin Bosslet) about 13 years ago
Thanks for investigating! I looked into it, here's what
I found.
String#tr!
doesn't work unfortunately. The original string's buffer
is deleted and the replacement takes place in a newly allocated
buffer [1]. I verified this in several core dumps, traces of the
password remain.
But the solution with IO#read
seems to work indeed. The contents
of the string are overwritten in place. My only concern is that
erasing the string in this manner is not a straightforward task.
You need to allocate a StringIO
and you must pay attention to
using String#bytesize
instead of String#size
, otherwise you might
be fooled by multi-byte encodings. Another issue is that it
would heavily rely on a side effect of the CRuby implementation
that could change over time and is maybe not applicable in other
implementations.
Having something explicit like String#clear_secure
is easier to
use, independent of side effects and self-documenting. However,
as Eric Hodel pointed out to me, using clear_secure
will erase
any String that referenced the original buffer. This is what we
actually want, but it should at least be documented to avoid
surprises.
Updated by MartinBosslet (Martin Bosslet) about 13 years ago
Links underlining that this is serious:
http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/password-not-wiped.html (thanks nahi!)
https://www.owasp.org/index.php/OWASP_Application_Security_FAQ (search for 'memory')
https://www.owasp.org/index.php/Insecure_Compiler_Optimization (cf. reference to "Writing Secure Code")
http://philosecurity.org/research/cleartext-passwords-linux
They list another situation when this can be exploited that I totally forgot: when memory gets swapped
out to disk.
Updated by Anonymous about 13 years ago
Here's my 2 yen.
- Would you agree that we need this functionality?
Good to have, but it would be hard to use properly.
- Where would we ideally place it? I'm not sure whether
String is the perfect place, but on the other hand, String
is the only place where we have access to the implementation
details.- Are there better alternative ways how we could achieve this?
I think you're going to adopt opt-in way, so library/application
developers must add String#clear
call after using the password, right?
If it's opt-in, new specific class would be enough I think. In this
way, we can control the memory copy (part-of, of course) and eventually
we might be able to split buffers into multiple parts that have
different addresses.
class SecureByteBuffer
def ==(rhs)
raise unless rhs.is_a?(SecureByteBuffer)
...
end
def clear
...
end
end
But the most hard part I think is how we construct this Object...
Martin, do you have concrete examples which needs secure erasure of
passwords? Only I can think of now is ossl_pem_passwd_cb
in
ext/openssl. It gets password as a String
from a callback block but it
would be good to add a feature to read from STDIN
directly, without
creating the String
object.
Best regards,
// NaHi
Updated by MartinBosslet (Martin Bosslet) about 13 years ago
Thanks for your thoughts!
I think you're going to adopt opt-in way, so library/application
developers must add String#clear call after using the password, right?
Yes, I think opt-in is the only way to ensure there are no negative side
effects on existing code.
If it's opt-in, new specific class would be enough I think. In this
way, we can control the memory copy (part-of, of course) and eventually
we might be able to split buffers into multiple parts that have
different addresses.class SecureByteBuffer
def ==(rhs)
raise unless rhs.is_a?(SecureByteBuffer)
...
enddef clear
...
end
end
That is an interesting approach, I'd love to catch up on this. So
far, I had four ideas for implementing such a thing.
- String#clear
- it's at the "root" of the problem
- adoption of the feature will merely ask for calling #clear on password variables at the right places
- invasive because rarely needed
- memory side effects of other string ops might be hard to keep in sync
- Decorator/Visitor/Subclass of String
- Unobtrusive
- But adoption requires a lot more code changes, not only the "erasure" part but also the parts
where the passwords are created
- Module/Class function class, "helper function"
- Unobtrusive
- Adoption requires only the additional calls at the place where erasure is needed
- Where to put this?
- Separate class, not necessarily String-like
- Unobtrusive
- Adoption again requires lots of changes (like in 2.)
- Will it be accepted by users?
If I understand correctly your proposal would tend towards 4.?
I'm open to anything, as long as there's something :) I haven't
looked at the memory copy issue yet in detail, so I can't really
tell for sure whether it's possible at all to control it entirely.
Might as well be the case that we would need to document safe
handling of passwords properly to ensure the desired outcome. I'd
volunteer for that once we have found a stable solution.
But the most hard part I think is how we construct this Object...
I know... :)
Martin, do you have concrete examples which needs secure erasure of
passwords? Only I can think of now is ossl_pem_passwd_cb in
ext/openssl. It gets password as a String from a callback block but it
would be good to add a feature to read from STDIN directly, without
creating the String object.
Besides the cases within the OpenSSL extension that you mentioned, I was
mainly thinking about authentication in web apps. You typically retrieve
the password String from a request there. I need to dig deeper to come
up with some concrete examples but I assume the idea should be clear.
Besides that I kept thinking about what you said the other day, whether
I'm only concerned about passwords or also about private keys and the like.
I was only thinking passwords when I wrote this, but you're absolutely right,
private keys in memory are equally bad [1].
It happens a lot that we use private keys in this way, SSH, SSL, digital signatures,
encryption, you name it. And the biggest problem is that the private keys are
not strings in general, so the String solution wouldn't apply directly. But I'm
confident that we could at least apply the same technique. Fortunately, OpenSSL takes
care of this for us in all places where we rely on OpenSSL (with its OPENSSL_cleanse
function), but still we should investigate if there aren't any loopholes left.
[1] http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.77.3297&rep=rep1&type=pdf
Updated by mame (Yusuke Endoh) over 12 years ago
- Status changed from Open to Assigned
- Assignee set to matz (Yukihiro Matsumoto)
I think that adding a method to String requires matz's approval.
If you propose to add a method to others, such as openssl, you
can do it at your discretion.
--
Yusuke Endoh mame@tsg.ne.jp
Updated by MartinBosslet (Martin Bosslet) over 12 years ago
mame (Yusuke Endoh) wrote:
I think that adding a method to String requires matz's approval.
If you propose to add a method to others, such as openssl, you
can do it at your discretion.
Sure - I would appreciate to have more opinions on how this can be
done best. I haven't really had the time to think it through yet,
but unfortunately I see two major obstacles for either approach.
-
I'm having doubts if this could be handled reliably within String
itself due to copy on write behavior. So when we finally call
something like String#clear, we would also need to clear any
proliferated instances, too. I haven't analyzed it yet, maybe
someone could tell me if that's possible at all? -
If we implement the feature separately (SecureByteBuffer), there
is a serious chicken & egg problem: how can we feed the password
to the implementation without using a String first? It's not as
easy as it may sound and it would require massive changes in
existing code - I fear this could be too invasive for anyone
to adopt it...
Any thoughts?
Updated by nobu (Nobuyoshi Nakada) over 12 years ago
What kind of methods will be needed for "Secure Password", do you think?
Is there any reason that it has to be real String
instance?
For example, I guess it should be BINARY or US-ASCII always, so shouldn't have encode method.
sub string of it makes no sense, so no []
, slice
, and etc.
sub
, tr
, upcase
, et al too.
Of course, to_sym
must be prohibited as it makes the string permanent.
Updated by MartinBosslet (Martin Bosslet) over 12 years ago
nobu (Nobuyoshi Nakada) wrote:
What kind of methods will be needed for "Secure Password", do you think?
Is there any reason that it has to be realString
instance?For example, I guess it should be BINARY or US-ASCII always, so shouldn't have encode method.
sub string of it makes no sense, so no[]
,slice
, and etc.
sub
,tr
,upcase
, et al too.
Of course,to_sym
must be prohibited as it makes the string permanent.
Totally agreed. No, there's no reason that it has to be a String instance. As
you already stated, it's more like a "byte array" - no need for encoding
awareness and it shouldn't respond to any methods that potentially hand out
parts of its contents.
I thought a bit more how to implement such a "Secure Password" class. How
can we "trap" the password in it initially without creating a String
first?
It seems not that easy, we can't simply do sec_pass.pwd = "password"
. Nor
can we reuse any IO - the buffer is again a String... So it seems to trap a
password in its SecurePassword
jail, we'd have to invent something really
awkward on top of that class :( Worse, I imagine nobody would likely want
to comb through their entire projects trying to find places where they have
to replace String
usage with SecurePassword
.
That's why I had hopes that the housekeeping could probably be done in String
.
It's a mess really, hopefully some of you have better ideas how this could be
solved in a more elegant way?
Updated by MartinBosslet (Martin Bosslet) over 12 years ago
Just to add this, I think it wasn't mentioned yet: we
also have to be aware of copying during GC, this could
compromise a password in memory as well.
Updated by kenkeiter (Ken Keiter) over 12 years ago
Is there any update on the status of this issue? This is simply a layperson's perspective (I'm not an expert regarding Ruby's internals), but I'd imagine this to be as easy as querying GC for all copies of the object, and overwriting the memory locations upon request?
I can dig into the internals if I need to; I'd just love to get this on the roadmap!
Updated by MartinBosslet (Martin Bosslet) over 12 years ago
Hi Ken,
I guess it largely depends on how things will go with #6361.
But if you are interested in the feature, we are beginning to work on
something that covers both the aspects of this issue and that of #6361.
You may want to keep an eye on https://github.com/emboss, it's going
to be called something like 'binary-io'.
Updated by ko1 (Koichi Sasada) about 12 years ago
ping. status?
I think matz doesn't know this ticket.
Could someone grab this ticket?
Updated by MartinBosslet (Martin Bosslet) about 12 years ago
FWIW, this feature is going to be an integral part of "binyo" (https://github.com/krypt/binyo).
Updated by mame (Yusuke Endoh) about 12 years ago
- Target version changed from 2.0.0 to 2.6