- If the user who runs the UI knows the passwords, then I don't really see your security concerns. Who are you defending from?
- If the user who runs the UI does not know the passwords (and the passwords should be stored in the testcases themselves), then you're in a pickle - encrypting is good, but you have to store the password for decrypting somehow ... endless circle, there's no safe space. Even if the storing is safe, the weak points are definitely the transfer to browser (which will be done as plain string encapsulated in JSON no matter what) and the automated typing into the browser itself. Seriously, if you don't trust the machines the tests are run on, don't store the passwords.
However, for the first case (when the user has to type in the password into the UI), to minimize the possibility of threat and also for maximum convenience, I'd do this:
- create a singleton class
PasswordHolder
- a
PasswordHolder
would remember the passwords (given to them by JPasswordField
) in a Map<String, char[]>
or Map<String, Password>
where key is some password identifier and value is the password itself
- once a password would be accessed via
getPassword()
method, it's contents would get nullified
A quick sample implementation (for further improvement as I hope I won't forget to include anything important ... but it could happen). I think it kinda speaks for itself:
public class PasswordHolder {
private static PasswordHolder instance = null;
private final Map<String, Password> map = new HashMap<String, Password>();
private PasswordHolder() {
// nothing to do
}
public static PasswordHolder getInstance() {
if (instance == null) {
instance = new PasswordHolder();
}
return instance;
}
public void addPassword(String name, char[] pass) {
if (map.containsKey(name)) {
// throw some sort of Exception("Duplicate password name, man.")
}
map.put(name, new Password(pass));
}
public Password getPassword(String name) {
return map.get(name);
}
}
As the most convenient thing, I wrote Password
to be a CharSequence
so is useful with sendKeys(CharSequence keys)
. Unfortunately, sendKeys()
uses toString()
, so you have to make a String out of the password anyway (which is considered a bad practice).
public class Password implements CharSequence {
private final char[] pass;
private final BitSet nulled;
Password(char[] pass) {
this.pass = pass;
nulled = new BitSet(pass.length);
}
private void nullify(int start, int end) {
for (int i = start; i < end; i++) {
pass[i] = '\0';
}
nulled.set(start, end);
}
@Override
public int length() {
return pass.length;
}
@Override
public char charAt(int index) {
if (nulled.get(index)) {
// throw some Exception("That character has already been read!")
}
char ret = pass[index];
pass[index] = '\0';
nulled.set(index);
return ret;
}
@Override
public CharSequence subSequence(int start, int end) {
if (nulled.get(start, end).cardinality() > 0) {
// throw some Exception("Some of the characters has already been read!")
}
Password subPass = new Password(Arrays.copyOfRange(pass, start, end));
nullify(start, end);
return subPass;
}
@Override
public String toString() {
if (nulled.cardinality() > 0) {
// throw some Exception("Some of the characters has already been read!")
}
String str = new String(pass);
nullify(0, pass.length);
return str;
}
}