问题
I am learning about Security and looking at storing secrets in the clear.
When I retrieve the contents of a private field, it returns an Object. My mal code correctly assumes and casts the Object as an int, however if I change/parse the field type from int secretInt = 42;
to String secretInt = (new Integer(42).intValue()).tostring
the Mal code fails miserably.
EDIT: The unusual wrapping (new Integer(42).intValue()).tostring
is created by a automated parser, it is not written by a programmer.
how can I add robustness to Mal code so the assumption of the returned type is removed. Is this possible? I need to use this value as int param.
EDIT: 'String' is one example but the parser may choose a data-structure as suitably-inappropriate as byte[], char[].
This is my non-compliant code.
public final class SecretInClear implements Check4SecretsInClear {
//Non-Compliant: Secret int stored in Clear.
private final int secretInt = 42;
@Override
public boolean isSecretInt(int check) {
return (check == secretInt);
}
}
This is my mal code.
public class ReadClearSecret implements Tester {
//Example of running
public static void main(String[] args) {
String testResult = new ReadClearSecret().test(new SecretInClear());
System.out.println(testResult);
}
private Object readPrivateField(Object o, String fieldName) {
try {
Field field = o.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(o);
} catch(Exception e) {
throw new IllegalArgumentExecption(e);
}
@Override
public String test(final Object secretChecks) {
final Check4SecretsInClear check4SecretsInClear = (Check4SecretsInClear)secretChecks;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("class:").
append(check4SecretsInClear.getClass().getSimpleName());
boolean bSecretInt = false;
String s = "";
try {
int secretInt = (Integer)readPrivateField(check4SecretsInClear,"secretInt"); //<<< HERE! It's cast as an integer!!!
bSecretInt = check4SecretsInClear.isSecretInt(secretInt); //<<< HERE! Param must be an int.
} catch (ClassCastException e) {
s = "," + e.getClass().getSimpleName();
} finally {
stringBuilder.append(" int:").append(bSecretInt).append(s);
s = "";
}
return stringBuilder.toString();
}
}
EDIT:
Instead of casting (int) from readPrivateField()
. Instead I extract the string value String.valueOf(Object)
or Object.toString()
. I can then pass that string as a int param with new Integer(stringValue)
.
HOWEVER:
If the parser chooses to represent secretInt
as type byte[] the string value will be nuts and the mal code will be pwned. Any suggest to produce robustness against this?
回答1:
The return type of Field.get() is an Object. If you need to know its class you can call Field.getType()
but usually you don't need to know the type, only the information contained.
You could just do
String secret = "42";
or
@Override
public boolean isSecretInt(int check) {
return check / 6.0 == 7;
}
Don't use Integer to compare values, as this compare objects, not their values.
A shorter implementation
private Object readPrivateField(Object o, String fieldName) {
try {
Field field = o.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(o);
} catch(Exception e) {
throw new IllegalArgumentExecption(e);
}
}
BTW: "What do you get if you multiply 6 by 9", for those who have read the Hitchhiker's Guide to the Galaxy.
System.out.println(Integer.toString(6 * 9, 13));
prints
42
;)
回答2:
You can always use:
String value = String.valueOf(field.get(o));
To avoid caring what the type is and always give you a String.
回答3:
CREDIT: Peter Lawrey
If it is suitably inappropriate you know the answer, it can't be don't generically in code. You need to read the byte code of isSecretInt
to see how it is done, and for that a human is the simplest solution ;) –
来源:https://stackoverflow.com/questions/21910529/reflection-improvements-to-access-field-secret-when-field-type-is-unknown