코드 품질이 낮으면 예측할 수 없는 동작이 발생합니다. 사용자 입장에서는 사용 편의성이 떨어지는 것으로 나타나는 경우가 많습니다. 공격자에게는 예상치 못한 방법으로 시스템에 부담을 줄 수 있는 기회가 됩니다.
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는 생성자에서 호출되는 함수의 하위 클래스를 먼저 확인하기 때문입니다.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()
...
}
x = NULL
및 x != NULL
식은 항상 false가 됩니다.NULL
의 값은 불명확합니다. 이는 무엇과도 같지 않으며 다른 NULL
값과도 같지 않습니다. 또한 null
값은 다른 값과도 같지 않습니다.예제 2:다음 문은 항상 false가 됩니다.
checkNull BOOLEAN := x = NULL;
checkNotNull BOOLEAN := x != NULL;
==
또는 !=
이 아닌 equals()
메서드로 비교해야 합니다.==
또는 !=
을 사용하는데 이 연산자는 두 개체의 값이 아닌 참조를 비교합니다. 두 참조가 같지 않을 가능성이 큽니다.
if (args[0] == STRING_CONSTANT) {
logger.info("miracle");
}
==
및 !=
연산자는 같은 개체에 포함된 문자열을 비교하도록 사용할 때에만 예상대로 작동합니다. 이를 위한 가장 일반적인 방법은 인턴(intern)된 문자열에 대한 것이고, 여기에서 이 문자열은 String
클래스에 의해 유지되는 개체의 풀에 추가할 수 있습니다. 한 번 문자열이 인턴(intern)되면, 모든 문자열 사용은 동일한 개체를 사용하고 같은 연산자가 예상대로 작동합니다. 모든 문자열 리터럴 및 문자열 값 상수는 자동으로 인턴(intern)됩니다. 다른 문자열은 String.intern()
을 수동으로 호출하여 인턴할 수 있어 현재 문자열의 정규 인스턴스를 반환하고, 필요한 경우 하나를 생성합니다.