2

Following an audit on our Swing application, it appears that some passwords of our users remain in memory long after they logged in or even out.

One of the causes seem to be that Apache HttpClient's UsernamePasswordCredentials stores the password as a final String, preventing to programmatically wipe it from memory (see Why is char[] preferred over String for passwords?).

However since the Credentials interface it implements has a String getPassword() method, it does not seem possible to avoid the conversion to a String at some point.

Is there another way to avoid passing the password around as a String in this case?

We are using HttpClient 4.0.3 but it does not seem like this has changed in more recent versions.

Community
  • 1
  • 1
Didier L
  • 18,905
  • 10
  • 61
  • 103

2 Answers2

0

Apache says that initializing UsernamePasswordCredentials with the password String is deprecated

UsernamePasswordCredentials(String usernamePassword)
Deprecated.
(4.5) will be replaced with String, char[] in 5.0

UsernamePasswordCredentials(String userName, String password)
The constructor with the username and password arguments.

https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/auth/UsernamePasswordCredentials.html

Are you using the latest version of the library? If so, they would not store the password as String but rather char[].

Didier L
  • 18,905
  • 10
  • 61
  • 103
Edd
  • 1,350
  • 11
  • 14
  • We are not using the latest version, but version 5.0 is still in alpha. Indeed it seems they will completely change the declaration to use `char[]` everywhere in 5, but that does not really help for now. Also, the other constructor (that we are using) is not deprecated (yet). – Didier L Mar 17 '17 at 16:27
  • I'm thinking, how about subclassing it and overriding the getter to return a new String based off your char[]? You could pass an empty string to the super constructor and make the object serve your char[]. This way the String that is kept around is actually empty – Edd Mar 17 '17 at 16:34
  • The thing is that as soon as you convert it to a `String`, the `char[]` is copied and you don't have any guarantee as to when it will be wiped from memory. This would thus be only a workaround that helps by making the `String` as short-lived as possible. Note that it seems to work, but I would need more guarantees, which is not possible to have with this solution. – Didier L Mar 17 '17 at 17:32
  • According to Peter Lawry here http://stackoverflow.com/questions/5672688/can-we-avoid-interning-of-strings-in-java, because you don't use a String literal but rather a char[], the String won't be interned. Do you feel this fact strengthen the solution ? – Edd Mar 17 '17 at 17:42
  • No, there are no String literals involved here, and there is no reason for the password to be `intern()`ed. Using String's is mostly an issue with the garbage collector. – Didier L Mar 17 '17 at 17:50
  • Internalized strings would end up in the Permgen (Metaspace in recent Java version) and therefore kept around longer though? I think this solution is better than what you had before, waiting for 5.0 to roll out. – Edd Mar 17 '17 at 18:11
0

In the end I implemented the workaround proposed by Edd in the comments of his answer.

char[] password = …
final Credentials credentials = new UsernamePasswordCredentials(username, null) {
    @Override
    public String getPassword() {
        // AKCTAT-3791: this helps the GC to clear the String from the memory, as it will be used and dismissed immediately
        // Unfortunately Apache HTTP Client does not allow to pass the byte[] directly
        return new String(password);
    }
};
httpClient.getCredentialsProvider().setCredentials(ANY_AUTHSCOPE, (Credentials) auth.getCredentials());
// ... (do stuff with httpClient)
Arrays.fill(password, '\0');

It appears the GC removes it very quickly from memory, but it is still only a workaround without guarantee.

Didier L
  • 18,905
  • 10
  • 61
  • 103