equals()
方法比较框式基元,可能会导致意外行为。equals()
方法,而非使用运算符 ==
和 !=
。Java 规范具有关于框式转换的如下说明: Boolean
或 Byte
),则仅会缓存或记住一个值区间。对于值的子集,使用 ==
或 !=
会返回正确的值,而对于此子集外的所有其他值,将返回对象地址的比较结果。
...
Integer mask0 = 100;
Integer mask1 = 100;
...
if (file0.readWriteAllPerms){
mask0 = 777;
}
if (file1.readWriteAllPerms){
mask1 = 777;
}
...
if (mask0 == mask1){
//assume file0 and file1 have same permissions
...
}
...
Example 1
中的代码会使用框式基元 Integer
尝试比较两个 int
值。如果 mask0
和 mask1
都等于 100
,则 mask0 == mask1
将返回 true
。但是,如果 mask0
和 mask1
都等于 777
,则 mask0 == maske1
现在将返回 false
,因为这些值不在这些框式基元的缓存值范围内。NaN
进行对比始终是一个错误。NaN
进行比较,得出的计算结果将始终为 false
(!=
运算符是个例外,因为 NaN
未经排序,所以始终会得出 true
结果)。NaN
。
...
if (result == Double.NaN){
//something went wrong
throw new RuntimeException("Something went wrong, NaN found");
}
...
result
并非 NaN
,但是使用带有 NaN
的运算符 ==
将始终得出 false
值,所以此检查将永远不会抛出异常。this
引用,进而导致出现漏洞。
...
class User {
private String username;
private boolean valid;
public User(String username, String password){
this.username = username;
this.valid = validateUser(username, password);
}
public boolean validateUser(String username, String password){
//validate user is real and can authenticate
...
}
public final boolean isValid(){
return valid;
}
}
validateUser
和该类并非 final
,这意味着它们是可以被覆盖的,对将覆盖此该函数的子类的变量执行初始化会允许避开 validateUser
功能。例如:
...
class Attacker extends User{
public Attacker(String username, String password){
super(username, password);
}
public boolean validateUser(String username, String password){
return true;
}
}
...
class MainClass{
public static void main(String[] args){
User hacker = new Attacker("Evil", "Hacker");
if (hacker.isValid()){
System.out.println("Attack successful!");
}else{
System.out.println("Attack failed");
}
}
}
Example 1
中的代码会输出“Attack successful!”,因为 Attacker
类会覆盖从超类 User
的构造函数调用的 validateUser()
函数,并且 Java 将首先在子类中查找从该构造函数调用的函数。
if (fitz == null) {
synchronized (this) {
if (fitz == null) {
fitz = new Fitzer();
}
}
}
return fitz;
Fitzer()
对象,但又不希望每次调用该代码时都进行一次同步。这就是所谓的 double-checked locking 方法。Fitzer()
对象。有关更多详细信息,请参见 The "Double-Checked Locking is Broken" Declaration [1]。inputReader
对象的类名称确认是否信任该对象的输入。如果攻击者能够提供一种执行恶意命令的 inputReader
实现方式,则此代码将无法区分该对象是善意的还是恶意的。
if (inputReader.GetType().FullName == "CompanyX.Transaction.Monetary")
{
processTransaction(inputReader);
}
inputReader
对象的类名称确认是否信任该对象的输入。如果攻击者能够提供一种执行恶意命令的 inputReader
实现方式,则此代码将无法区分该对象是善意的还是恶意的。
if (inputReader.getClass().getName().equals("com.example.TrustedClass")) {
input = inputReader.getInput();
...
}
inputReader
对象的类名称确认是否信任该对象的输入。如果攻击者能够提供一种执行恶意命令的 inputReader
实现方式,则此代码将无法区分该对象是善意的还是恶意的。
if (inputReader::class.qualifiedName == "com.example.TrustedClass") {
input = inputReader.getInput()
...
}
finalize()
方法应该调用 super.finalize()
。finalize()
方法调用 super.finalize()
是一种非常好的做法 [1]。super.finalize()
。
protected void finalize() {
discardNative();
}