1

Java provides the URLEncoder class for URL-encoding Strings. But it is considered insecure to store passwords as Strings. Is this code to send a password via POST over an HttpsURLConnection output stream wr secure enough?

try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) {
    // Write some post params
    for (char c : pass) {
        wr.writeBytes(URLEncoder.encode(String.valueOf(c), "UTF-8"));
    }
    // Write some more
}

On the one hand, it is using Strings. On the other hand, those Strings are 1 character long, and conceptually the same after encoding. Also, it seems to me that this could fail on multi-byte characters. Would an attacker be able to locate these 1-char Strings in memory and reconstruct the original password? Is there a better way to do this?

Community
  • 1
  • 1
Brian McCutchon
  • 8,354
  • 3
  • 33
  • 45
  • I dont get your point: you are not talking about how to represent a password in memory. *You* are talking about *transmitting* passwords. That is *not* the same?! – GhostCat Feb 19 '17 at 18:42
  • @GhostCat I am talking about how to transmit a password without storing it in an unsafe format in memory. – Brian McCutchon Feb 19 '17 at 18:43
  • @GhostCat Specifically, I am wondering if it is secure to store the individual characters of my password as Strings. Would an attacker be able to locate these Strings and reconstruct the password? – Brian McCutchon Feb 19 '17 at 18:47
  • Well it's a theoretical attack. However the `URLEncoding` is completely irrelevant and useless here. If you're working with ASCII, you can just cast the chars to bytes and write them over the network. If you're working with unicode, you need to map the chars to bytes properly. – Kayaman Feb 19 '17 at 18:51
  • 1
    @Kayaman What do you mean when you say URL-encoding is useless? The string I'm sending looks like `user=jdoe&pass=12345&someparam=...`. By the time the code reaches the loop, it has written `user=jdoe&pass=`. If I don't URL-encode the password, it won't work. For example, what if the password is something like `my&pass=word`? – Brian McCutchon Feb 19 '17 at 18:58
  • You're sending it through a `DataOutputStream`. As the name implies, `URLEncoder` is needed when you need to encode data suitable for putting in `URL`. Here it's just a useless extra step. – Kayaman Feb 19 '17 at 19:00
  • @Kayaman Did you read my comment? I'm sending POST params. They have the same format as a URL. – Brian McCutchon Feb 19 '17 at 19:01
  • @Kayaman I have tested it without the URL-encoding and it does not work if the password contains special characters. – Brian McCutchon Feb 19 '17 at 19:02
  • Oh I see, it's a post param. Anyhow, the Strings are in theory observable in the memory for a while, but how top secret is your project to make it an issue you need to address? – Kayaman Feb 19 '17 at 19:09
  • @Kayaman Somebody who got this password might be able to log into an email account with it. – Brian McCutchon Feb 19 '17 at 19:35
  • But you do understand the details of the attack? One would need to be able to examine the memory of the process, make sure that he does it before GC has had a chance to collect the strings, and if you choose to write it one char at a time, combine the strings to get the full password. Is that a realistic attack vector? It's not bank/military/etc. software, so you probably don't need to be *that* paranoid. – Kayaman Feb 19 '17 at 19:40
  • @Kayaman What if someone uses the email address to set up their bank account? Someone who compromises the email could do a password reset on the bank. Email should be as secure as bank. Still, would someone need root to read the memory? Because if they have root my application is defenseless anyway. – Brian McCutchon Feb 19 '17 at 19:56
  • @Kayaman Also, my understanding is that GC != zeroing out. – Brian McCutchon Feb 19 '17 at 19:59
  • No, but eventually it'll get overwritten by new allocations / compacted by the GC / etc. The main issue an attacker would need to achieve is to read the process memory, and that's not something you can do easily. – Kayaman Feb 19 '17 at 20:02
  • @Kayaman Is it something you can do without root? If so, it seems a small cost to write a `urlEncode(char)` method to lessen this attack vector. – Brian McCutchon Feb 19 '17 at 20:07
  • Of course not. You don't get to read the memory used by processes that you're not authorized to. That would be a huge *security weakness*. – Kayaman Feb 19 '17 at 20:08
  • @Kayaman That's what I thought. In that case, I'm not even sure why I'm using `char[]` in my application to begin with (I'll keep doing it, though), as somebody with root could get the passwords anyways (e.g. via a keylogger). Can you write an answer summarizing your comments on the difficulty of this attack? I'll accept it. – Brian McCutchon Feb 19 '17 at 20:16
  • In Android devices, it is not required to root the device to capture memory dump. Just USB Debugging is to be enabled, and of course the end user must have accepted to do usb debugging with a laptop: https://developer.android.com/studio/profile/memory-profiler#capture-heap-dump – garnet Jun 26 '18 at 07:13
  • Also, the apk should have debuggable flag set to true. But, this is definitely not an issue which can be exploited by apps published in playstore since they will be in release mode. – garnet Jun 26 '18 at 08:31

2 Answers2

2

It's true that when using Strings for passwords, you can't reliably discard them since you can't zero out the String contents manually (except through reflection) and the contents will stay in memory for an unknown time. Therefore char[] is often recommended to be used for any password input, followed by a manual zeroing of the said char[].

However the attack is in no way easy to mount since it requires access to the memory, luck with timing. It's unlikely the password will stay in the memory for very long amounts of time, as the GC does its work and the memory gets reused. In most cases this attack vector can be infeasible compared to other, simpler attacks.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
0

An OutputStream has no method writeBytes; but it offers a write(byte[]) method that could be used to write all chars of the password with one call.

And beyond that: the whole idea of using HTTPS is that the connection itself is securely encrypted; so it really should not matter whether you transmit such content with single byte or multi byte bursts.

Then: keep in mind that all these classes are abstractions, that are layered upon each other. HTTPS uses TCP; and TCP packets have a certain size; in that sense there aren't single bytes going over the network anyway.

And regarding the second part of your question: you are iterating an array of char values. So it really depends how that char array was created; but normally, you don't need to worry (see here on that topic).

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Read the linked post. It's about storing passwords as Strings, not about security in transit. I'm actually using `DataOutputStream`. I'll add that to my question. – Brian McCutchon Feb 19 '17 at 18:39
  • The char array was created from e.g. `JPasswordField.getPassword()`. – Brian McCutchon Feb 19 '17 at 18:44
  • @BrianMcCutchon In other words: you can trust that this char array represents the password; because you rely on a well-test-trusted library component to get it from you. In other words: you do not need to worry that the individual chars in that array would fail to be represented with utf8. – GhostCat Feb 19 '17 at 18:54
  • Fair enough on that point, but that leaves my main question (security) unanswered. (The UTF-8 part was just something that occurred to me as I was writing the question.) – Brian McCutchon Feb 19 '17 at 19:00