Uma API é um contrato entre quem chama e o que se chama. As formas mais comuns de abuso de API ocorrem quando o responsável pela chamada não respeita sua parte do contrato. Por exemplo, se um programa não chama chdir() após chamar chroot(), ele viola o contrato que especifica como alterar o diretório raiz ativo de forma segura. Outro bom exemplo de abuso de biblioteca é esperar que o elemento chamado retorne informações confiáveis de DNS ao responsável pela chamada. Nesse caso, o responsável pela chamada abusa a API do elemento chamado ao fazer certas suposições sobre seu comportamento (isto é, que o valor de retorno pode ser usado para fins de autenticação). A outra parte também pode violar o contrato entre quem chama e o que se chama. Por exemplo, se um programador definir SecureRandom como subclasse e retornar um valor não aleatório, o contrato será violado.
Missing Check against Null
null
.Duas suposições duvidosas que são fáceis de detectar no código são "essa chamada de função nunca pode falhar" e "não importa se essa chamada de função falhar". Quando um programador ignora o valor de retorno de uma função, ele afirma implicitamente estar operando de acordo com uma dessas suposições.
Exemplo 1: O código a seguir não verifica se a string retornada pela propriedade
Item
é null
antes de chamar a função membro Equals()
, causando possivelmente um cancelamento de referência null
string itemName = request.Item(ITEM_NAME);
if (itemName.Equals(IMPORTANT_ITEM)) {
...
}
...
A defesa tradicional desse erro de codificação é:
"Sei que o valor solicitado sempre existirá porque... Se não existir, o programa não poderá executar o comportamento desejado e, portanto, não importa se eu manipular o erro ou simplesmente permitir que o programa se torne inativo ao tentar desfazer a referência a um valor
null
."Porém, os invasores são hábeis em encontrar caminhos inesperados através de programas, particularmente quando exceções estão envolvidas.
null
.Duas suposições duvidosas que são fáceis de detectar no código são "essa chamada de função nunca pode falhar" e "não importa se essa chamada de função falhar". Quando um programador ignora o valor de retorno de uma função, ele afirma implicitamente estar operando de acordo com uma dessas suposições.
Exemplo 1: O código a seguir não verifica se a alocação de memória foi bem-sucedida antes de tentar usar o ponteiro retornado por
malloc()
.
buf = (char*) malloc(req_size);
strncpy(buf, xfer, req_size);
A defesa tradicional desse erro de codificação é:
"Se meu programa ficar sem memória, ele falhará. Não importa se eu tratar o erro ou simplesmente permitir que o programa se torne inativo com uma falha de segmentação ao tentar desreferenciar o ponteiro nulo."
Esse argumento ignora três considerações importantes:
- Dependendo do tipo e do tamanho do aplicativo, pode ser possível liberar a memória que está sendo usada em outro local para que a execução possa continuar.
- É impossível para o programa realizar uma saída normal, se necessário. Se o programa realizar uma operação atômica, ele poderá deixar o sistema em estado inconsistente.
- O programador perdeu a oportunidade de registrar informações de diagnóstico. A chamada para
malloc()
falhou por que req_size
era grande demais ou por que muitas solicitações estavam sendo tratadas ao mesmo tempo? Ou ela foi causada por um vazamento de memória que se acumulou com o passar do tempo? Sem realizar o tratamento do erro, não há como saber.null
.Duas suposições duvidosas que são fáceis de detectar no código são "essa chamada de função nunca pode falhar" e "não importa se essa chamada de função falhar". Quando um programador ignora o valor de retorno de uma função, ele afirma implicitamente estar operando de acordo com uma dessas suposições.
Exemplo 1: O código a seguir não verifica se a string retornada por
getParameter()
é null
antes de chamar a função membro compareTo()
, causando possivelmente um cancelamento de referência null
.Exemplo 2:. O código a seguir mostra uma propriedade do sistema que é definida como
String itemName = request.getParameter(ITEM_NAME);
if (itemName.compareTo(IMPORTANT_ITEM)) {
...
}
...
null
e, mais tarde, tem sua referência desfeita por um programador, que erroneamente assume que ela sempre será definida.
System.clearProperty("os.name");
...
String os = System.getProperty("os.name");
if (os.equalsIgnoreCase("Windows 95") )
System.out.println("Not supported");
A defesa tradicional desse erro de codificação é:
"Sei que o valor solicitado sempre existirá porque... Se não existir, o programa não poderá executar o comportamento desejado e, portanto, não importa se eu manipular o erro ou simplesmente permitir que o programa se torne inativo ao tentar desfazer a referência a um valor
null
."Porém, os invasores são hábeis em encontrar caminhos inesperados através de programas, particularmente quando exceções estão envolvidas.