43

An empoyee at my company needs to modify data from a SQL Server database through a program I made. The program used Windows authentication at first, and I asked the DBAs to give this specific user write access to said database.

They were not willing to do this, and instead gave write access to my Windows user account.

Since I trust the guy but not enough to let him work 90 minutes with my session open, I'll just add a login prompt to my program, asking for a username and password combination, and log in to SQL Server with it. I'll log in, and trust my application to let him do only what he needs to.

This, however, raises a small security risk. The password fields tutorial over SunOracle's site states that passwords should be kept the minimum amount of time required in memory, and to this end, the getPassword method returns a char[] array that you can zero once you're done with it.

However, Java's DriverManager class only accepts String objects as passwords, so I won't be able to dispose of the password as soon as I'm done with it. And since my application is incidentally pretty low on allocations and memory requirements, who knows how long it'll survive in memory? The program will run for a rather long time, as stated above.

Of course, I can't control whatever the SQL Server JDBC classes do with my password, but I hoped I could control what I do with my password.

Is there a surefire way to destroy/zero out a String object with Java? I know both are kind of against the language (object destruction is non-deterministic, and String objects are immutable), and System.gc() is kind of unpredictable too, but still; any idea?

zneak
  • 134,922
  • 42
  • 253
  • 328
  • 6
    so you are assuming an evil doer can peek into your app's memory, and you are trying to defeat him in that situation? you are trying to find an improbable solution to an improbable threat. the evil doer has much simpler way to get the password, if he has that kind of access to the computer. The only effective solution is to shield the computer in a big tin foil hat. – irreputable Mar 08 '11 at 21:28
  • 1
    It goes without saying but I'm sure you are aware that many companies have strict policies about password sharing. Of course we all do it from time to time - but on an ongoing basis isn't the simpler route to escalate the issue & get the account set up you require. I'd imagine reflection is probably the route to adopt - as others have answered. – David Victor Mar 08 '11 at 21:30
  • 2
    @irreputable I have to agree that it won't matter much in this case (as I have already commented on an answer), but if I ever come to a more paranoid environment, such a situation could happen again. – zneak Mar 08 '11 at 21:31
  • @irreputable Also, if you're just looking for a realistic scenario, just imagine that the user attaches a debugger to my process and scans the raw memory once I'm gone. – zneak Mar 08 '11 at 21:35
  • 2
    Is that realistic? Only you know your user. But if I were you, I'll provide a remote interface. If he can access the computer your app is running on, and he is that resourceful and determined, you are toast. – irreputable Mar 08 '11 at 21:51
  • 1
    @irreputable It's not realistic in my case, the user's a nice guy. Does it make it inappropriate to ask the question? – zneak Mar 08 '11 at 21:54
  • 1
    I think y'all have missed the larger picture. There are plenty of security situations where you are mandated by the customer to ensure any in-clear data is permanently erased from memory before you exit. In C/C++, that's not too hard, but I've rarely seen any reliable examples of how to do so in Java. Which is a shame. I really prefer Java over C/C++. – EdwinW Nov 10 '13 at 03:38
  • I'm developing a software & **exact same requirement** rose, this is awesome Questions. Well asked with precise words! – InamTaj Oct 01 '14 at 17:24

9 Answers9

25

So, here's the bad news. i'm surprised no one has mentioned it yet. with modern garbage collectors, even the whole char[] concept is broken. regardless of whether you use a String or a char[], the data can end up living in memory for who-knows-how-long. why is that? because modern jvms use generational garbage collectors which, in short, copy objects all over the place. so, even if you use a char[], the actual memory it uses could get copied to various locations in the heap, leaving copies of the password everywhere it goes (and no performant gc is going to zero out old memory). so, when you zero out the instance you have at the end, you are only zeroing out the latest version in memory.

long story, short, there's no bulletproof way to handle it. you pretty much have to trust the person.

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
  • Any references for memory not being zeroed out? After all, the memory must be zero when a new object is allocated so all fields get their initial value. I always assumed this would be achieved by zeroing the memory during GC. – meriton Mar 12 '11 at 01:37
  • 3
    @meriton - i remember seeing an article detailing some tests related to this a while ago, but couldn't find it the last time i looked. as for zeroing out memory, not all memory use will be new object allocation. for instance, when existing objects are copied from one generation to another, there's no need for the destination to be zeroed out beforehand. – jtahlborn Mar 14 '11 at 15:11
  • Hi @jtahlborn, I'm new to Java, so maybe this is a poor question: Is there a way to suspend garbage collection for specific code. i.e. tell the JVM not to run GC until my 'authenticate' method terminated? Also, as the answer is 9 years old, is it still true, there is no good handling for passwords reveal looking at process memory? – Eliyahu Machluf Aug 25 '20 at 14:28
  • @EliyahuMachluf - yes, this answer is still relevant. i don't believe any standard jvms allow you to suspend garbage collection for a specific period of time. i'm pretty sure that some jvms targetted at the embedded space do have more control over the gc, so i wouldn't be surprised if some niche jvms provide this functionality. i would still re-examine why you think you need this functionality, because i doubt the reasoning holds up. – jtahlborn Aug 25 '20 at 14:57
12

I can only think of a solution using reflection. You can use reflection to invoke the private constructor that uses a shared character array:

  char[] chars = {'a', 'b', 'c'};
  Constructor<String> con = String.class.getDeclaredConstructor(int.class, int.class, char[].class);
  con.setAccessible(true);
  String password = con.newInstance(0, chars.length, chars);
  System.out.println(password);

  //erase it
  Arrays.fill(chars, '\0');
  System.out.println(password);

Edit

For anyone thinking this is a failproof or even useful precaution, I encourage you to read jtahlborn's answer for at least one caveat.

Community
  • 1
  • 1
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • That won't prevent the driver to create a copy with the passed value `new String( password )` This is a JDBC driver matter. – OscarRyz Mar 08 '11 at 21:17
  • 1
    @OscarRyz There is nothing to do against that, so I'll have to trust the guys at Microsoft. – zneak Mar 08 '11 at 21:19
  • @Oscar: Agreed, but I really don't think the OP is worried about what the driver is going to go off and do. And I wouldn't say it's a driver matter, I'd say it's an API matter. Presumably the designers thought that if the connections were being obtained by untrusted code then you've given up on security anyway. – Mark Peters Mar 08 '11 at 21:19
  • +1 I think the original question is a bit misleading, but interesting anyway. After this I will make sure to create a copy of the string I receive :) – OscarRyz Mar 08 '11 at 22:16
5

if you absolutely must, keep a WeakReference to the string, and keep gobbling memory until you force garbage collection of the string which you can detect by testing if the weakreference has become null. this may still leave the bytes in the process address space. may be a couple more churns of the garbage collector would give you comfort? so after your original string weakreference got nulled, create another weakreference and churn until it is zeroed which would imply a full garbage collection cycle was done.

somehow, i have to add LOL to this even though my answer above is entirely serious :)

necromancer
  • 23,916
  • 22
  • 68
  • 115
  • 1
    btw, re reflection answers above, they are assuming that the JVM you are running has a specific implementation of string which has those private/protected members. – necromancer Mar 08 '11 at 22:01
3

I am not sure on the DriverManager class.
Generally speaking, you are right, the recomendation is to store the password in char arrays and to explicitely clear the memory after usage.
The most common example:

KeyStore store = KeyStore. getInstance(KeyStore, getDefaultType()) ;
char[] password = new char[] {'s','e','c','r','e','t'};
store .load(is, password );
//After finished clear password!!!
Arrays. fill(password, '\u0000' ) ;

In the JSSE and JCA the design had exactly this in mind. That is why the APIs expect a char[] and not a String.
Since, as you very well know Strings are immutable, the password is eligible for future garbage collection and you can not reset it afterwards. This can cause security risks by malicious programs that snoop memory areas.
I do not think in this case you are looking into there is a work around.
Actually there is a similar question here:
Why Driver Manager not use char arrays?
but there is no clear answer.
It appears that the concept is that the password is already stored in a properties file (there is already a DriverManager constructor accepting properties) and so the file itself already imposes a bigger risk than the actual loading the password from a file to a string.
Or the designers of the API had some assumptions on the safety of the machine accessing the DB.
I think the safest option would be to try to look into, if it is possible, on how the DriverManager uses the password i.e. does it hold on to an internal reference etc.

Community
  • 1
  • 1
Cratylus
  • 52,998
  • 69
  • 209
  • 339
2

You can change the value of the inner char[] using reflection.

You must be careful to either change it with an array of the same length, or to also update the count field. If you want to be able to use it as an entry in a set or as a value in map, you will need to recalculate the hash code and set the value of the hashCode field.

That being said, the minimal code to achieve this is

        String password = "password";

        Field valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);
        char[] chars  = (char[]) valueField.get(password);

        chars[0] = Character.valueOf('h');

        System.out.println(password);
Robert Munteanu
  • 67,031
  • 36
  • 206
  • 278
  • 1
    Won't that minimal code just "leak" the older char array? I'd be stuck with the same problem: my cleartext password existing somewhere in memory. – zneak Mar 08 '11 at 21:06
  • Re Problem 1 and 2. I believe there is a length field in String that you have to update in addition to char[]. Just pull up String source. – Konstantin Komissarchik Mar 08 '11 at 21:12
  • @Konstantin: more precisely, count and hash should be updated for the String to work exactly as intended. – Robert Munteanu Mar 08 '11 at 21:14
  • Now, this is the first serious response to the security aspect! Though, thought ´Arrays.fill(chars, (char) 0);´ actually overwrites the password and grants a clean String instance. With subsequent ´password = null;´ the inconsistency of ´hashCode´ is clamped. – Sam Ginrich Aug 07 '21 at 11:22
0

Interesting question. Some googeling revealed this: http://securesoftware.blogspot.com/2009/01/java-security-why-not-to-use-string.html. According to the comment, it won't make a difference.

What happens, if you dont store the String in a variable but pass it via new String(char[])?

Jan Galinski
  • 11,768
  • 8
  • 54
  • 77
  • 1
    Thanks for your interest; however, my conerns are of security nature. If a process somehow accessed my program's address space, they could find my password in plain text there. MVC isn't going to solve that. – zneak Mar 08 '11 at 21:02
0

There's no regular way of forcing garbage collection. It is possible through a native call but I don't know if it would work for Strings, seeing as they are pooled.

Maybe an alternative approach will help? I'm not that familiar with SQL Server but in Oracle the practice is to have user A own the database objects and a set of stored procedures, and user B owning nothing but given run permission on the procedures. This way the password isn't really a problem anymore.

In your case, it will be your user who owns all the database object and stored procedures and your employee will need run privileges to the stored procs, which the DBAs will hopefully be less reluctant to give.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • Can't say this is the best way to go either, but this situation is rather exceptional anyways. That said, I'm not really afraid that anything will go wrong, I'm just trying to see how to come over this problem if I ever have to do something else similar in a more risky environment. – zneak Mar 08 '11 at 21:15
  • @zneak What I'm trying to say is that if the DBAs don't want to give him/her write access, then you shouldn't really do it either. In a way it undermines their job. – biziclop Mar 08 '11 at 21:18
  • They're fully aware of the situation, so I'm not undermining anything. That's not an issue. – zneak Mar 08 '11 at 21:22
  • 1
    @zneak Is the reason a three letter word that starts with I and ends in SO? :) This is all I can help I'm afraid then, I checked whether the MSSQL Driver has an implementation-specific way of passing a password but I had no success. – biziclop Mar 08 '11 at 21:25
0

so I won't be able to dispose of the password as soon as I'm done with it.

Why not?

If you're getting a connection via

Driver.getConnection 

you just have to pass the password and let it be gc'ed.

void loginScreen() {
   ... 
   connect( new String( passwordField.getPassword() ) );
   ...
}
...
void connect( String withPassword ) { 
    connection = DriverManager.getConnection( url, user, withPassword );
    ...
}//<-- in this line there won't be any reference to your password anymore.

When the control return from the connect method, there is no longer a reference for your password. No one can use it anymore. If you need to create a new session, you have to invoke "connect" again with a new password. The reference to the object that holds your information is lost.

OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • 4
    Oracle explicitly suggests to _not_ wait until it is GCed. The main reason `JPasswordField` returns a `char[]` is that the data in character arrays are mutable, and you can zero out the bytes once you're done with them. These bytes can be dangerous to keep around in memory, as there is a possibility that malware can access it. – zneak Mar 08 '11 at 21:04
  • But if you don't keep a reference to it, it won't be used, and your password is safe! – OscarRyz Mar 08 '11 at 21:08
  • 1
    It won't be safe if malware can read another process's address space. They don't need Java references to access raw memory. – zneak Mar 08 '11 at 21:13
  • In the JDBC driver it self? If you don't trust your JDBC driver to hold a password, pick another, you can't control that. – OscarRyz Mar 08 '11 at 21:14
  • @Oscar: It doesn't have to be the JDBC driver. It could be any privileged application or any application running on an insecure OS. You never know when something will be GC'd, and just because something is GC'd doesn't mean the underlying data doesn't exist in memory. – Mark Peters Mar 08 '11 at 21:22
  • In that case you're looking at the wrong place I think, is not your app you have to secure, but your platform. – OscarRyz Mar 08 '11 at 21:28
  • Another, more realistic scenario would be that the user attaches a debugger to the process and looks at raw memory once I'm gone. – zneak Mar 08 '11 at 21:34
0

If the string is not being held onto by JDBC driver manager (a big if), I wouldn't worry about forcing its destruction. A modern JVM, still runs the garbage collection fairly promptly even with plenty of available memory. The issue is whether garbage collection is effective "secure erase". I doubt that it is. I would guess that it simply forgets the reference to that memory location and doesn't zero anything out.

Konstantin Komissarchik
  • 28,879
  • 6
  • 61
  • 61
  • Thanks for reassuring me! haha – zneak Mar 08 '11 at 21:07
  • Actually, garbage collection is relatively safe, at least in the young generation, as it involves continuously copying data between the two halves of survivor space, until tenured to the old generation. In any case, it's not very predictable or controllable, just a curiosity. – biziclop Mar 08 '11 at 21:22