Códigos de baixa qualidade levam a comportamentos imprevisíveis. Da perspectiva do usuário, isso normalmente se manifesta como usabilidade ruim. Para um invasor, trata-se de uma oportunidade para atacar o sistema de formas imprevistas.
NaN
é sempre um erro.NaN
, ela é sempre avaliada como false
, exceto para o operador !=
, que sempre é avaliado como true
, já que NaN
não está ordenado.NaN
.
...
if (result == Double.NaN){
//something went wrong
throw new RuntimeException("Something went wrong, NaN found");
}
...
result
não é NaN
, mas o uso do operador ==
com NaN
sempre resulta em um valor de false
, e, portanto, essa verificação nunca lançará a exceção.this
antes que o objeto seja totalmente inicializado, o que, por sua vez, pode provocar uma vulnerabilidade.
...
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
e a classe não são final
, isso significa que elas podem ser substituídas e, dessa forma, inicializar uma variável para a subclasse que substitui essa função possibilitaria o desvio da funcionalidade validateUser
. Por exemplo:
...
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
imprime "Attack successful!", uma vez que a classe Attacker
substitui a função validateUser()
que é chamada a partir do construtor da superclasse User
, e o Java primeiro examinará a subclasse em busca de funções chamadas a partir desse construtor.inputReader
é confiável com base em seu nome de classe. Se um invasor puder fornecer uma implementação de inputReader
que executa comandos mal-intencionados, esse código não poderá diferenciar as versões bem-intencionadas das mal-intencionadas do objeto.
if (inputReader.GetType().FullName == "CompanyX.Transaction.Monetary")
{
processTransaction(inputReader);
}
inputReader
é confiável com base em seu nome de classe. Se um invasor puder fornecer uma implementação de inputReader
que executa comandos mal-intencionados, esse código não poderá diferenciar as versões bem-intencionadas das mal-intencionadas do objeto.
if (inputReader.getClass().getName().equals("com.example.TrustedClass")) {
input = inputReader.getInput();
...
}
inputReader
é confiável com base em seu nome de classe. Se um invasor puder fornecer uma implementação de inputReader
que executa comandos mal-intencionados, esse código não poderá diferenciar as versões bem-intencionadas das mal-intencionadas do objeto.
if (inputReader::class.qualifiedName == "com.example.TrustedClass") {
input = inputReader.getInput()
...
}
x = NULL
e x != NULL
serão sempre false.NULL
é indeterminado. Não será equivalente a nada, nem mesmo a um outro valor NULL
. Além disso, um valor null
nunca é equivalente a outro valor.Exemplo 2: Esta instrução será sempre false.
checkNull BOOLEAN := x = NULL;
checkNotNull BOOLEAN := x != NULL;
equals()
, e não com ==
ou !=
.==
ou !=
para comparar a igualdade de duas strings, o que compara a igualdade de dois objetos, e não seus valores. São boas as chances de que as duas referências nunca serão iguais.
if (args[0] == STRING_CONSTANT) {
logger.info("miracle");
}
==
e !=
apenas se comportarão conforme esperado quando forem usados para comparar strings contidas em objetos que são iguais. A maneira mais comum de isso acontecer é internalizando as strings, processo pelo qual elas são adicionadas a um pool de objetos mantidos pela classe String
. Após a internalização de uma string, todas as suas aplicações usarão o mesmo objeto, e os operadores de igualdade terão o comportamento esperado. Todos os literais de string e constantes com valores de string são internalizados automaticamente. Outras strings podem ser internalizadas manualmente chamando String.intern()
, o que retornará uma instância canônica da string atual, criando uma se necessário.