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
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);
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 :)
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.
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);
For anyone thinking this is a failproof or even useful precaution, I encourage you to read jtahlborn's answer for at least one caveat.
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.
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.