A4 Insecure Direct Object References

Artigo sobre a vulnerabilidade Referências Inseguras Diretas a Objetos, quarta da lista TOP 10 da WOASP

A Referências Inseguras Diretas a Objeto ocorre quando o desenvolvedor expõe uma referência a objetos internos da aplicação web e o atacante consegue alterar esse parâmetro obtendo, dessa forma, acesso a informações confidenciais. Os objetos internos podem ser, por exemplo, um arquivo, um diretório ou um registro do banco de dados exposto através de uma URL ou formulário. O atacante é um usuário autorizado no sistema que altera o valor de um parâmetro, que se refere diretamente a um objeto no sistema, para um outro objeto na qual não teria autorização. O impacto é considerado moderado pois a exploração desta vulnerabilidade pode comprometer todos os dados que são referenciados através de parâmetros, a tabela abaixo sintetiza a classificação do risco.

Tabela de risco Referências Inseguras Diretas a Objetos

Para descobrir se uma aplicação é vulnerável a Referências Inseguras Diretas a Objetos é preciso verificar se todas as referências a objetos possuem defesas próprias.

Essas defesas consistem em...

  1. verificar se o usuário está autorizado a aceder o recurso que foi solicita e
  2. se a referência é uma referência indireta

O mapeamento para a referência direta deve ser limitada aos valores autorizados para o usuário atual. Para que a revisão do código seja eficiente é preciso considerar as duas abordagens de defesas. Teste manuais também são igualmente eficientes. Os teste automáticos não são indicados, pois não procuram este tipo de falha uma vez que não reconhecem o que necessita de proteção.

Exemplo de aplicação vulnerável

O atacante acessa a página de cadastro de determinado cliente com chave de identificação de número 1015, por exemplo. Ele percebe que o parâmetro que recupera o cliente da base de dados está sendo enviado via método post e chama-se idCliente. A chave de registro da tabela clientes é do tipo numérica e sequencial, logo, o atacante percebe que se mudar o parâmetro de 1015 para 1014 o sistema retorna o registro do cliente de chave número 1014. O atacante continua testando outros valores, os que coincidirem com os da tabela clientes a aplicação mostrará os registro, indevidamente. O código abaixo ilustra a aplicação vulnerável. Ele é apenas um trecho de código, as demais partes foram suprimidas para simplificação do entendimento. A linha 02 recebe os dados, no caso a chave de cada registro de cliente. As linhas 4, 5 e 6 montam executam a instrução SQL. Repare que mesmo a aplicação estando protegida contra Injeção (vide A1- Injeção) ele não está, necessariamente, protegida contra Referências Inseguras Diretas a Objetos.

1  
2 <?php
3 $idCliente = $_POST['idCliente'];
4 
5 $sth = $dbh->prepare("SELECT * FROM clientes ".
6                      "WHERE idCliente = :idCliente");
7 $sth->bindParam(':idCliente', $idCliente);
8 $sth->execute();

Outro exemplo de aplicação vulnerável é ilustrado pelo formulário do código abaixo. O formulário envia através do método get o valor do controle HTML do tipo "select" denominado idioma. O script PHP responsável por trocar o idioma faz acesso direto ao objeto tornando, dessa forma, o código vulnerável.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="pt-br">
  <head>
    <title>Escolha um idioma</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  </head>
  <body>
      <form action="alterarIdioma.php" method="get">
          <p>Formulário que altera o idioma.</p>
          <p>
          <select name="idioma">
              <option value="en">Inglês</option>
              <option value="pt">Português</option>
          </select>
          </p>
          <p><input type="submit" value="Efetuar login" /></p>
      </form>
  </body>
</html>

O atacante poderia alterar o parâmetro para, por exemplo, /etc/passwd e assim obter acesso ao arquivo de usuários do sistema operacional Linux, conforme ilustra o código abaixo.

<?php

require $_REQUEST['idioma'];

# demais código

?>

Prevenção

Segundo o OWASP Top 10 (2010) há duas formas básicas de evitar esta vulnerabilidade:

  1. usar referências indiretas a objetos e
  2. verificar o acesso ao objeto.

Já o OWASP Top 10 (2007) nos diz que a proteção mais eficaz a esta vulnerabilidade seria evitar a exposição direta de referências a objetos a usuários usando um índice, mapa de referência indireta ou outro método indireto que seja fácil validar. Ainda como prevenção o OWASP Top 10 (2007) reitera que, é necessário considerar as recomendações como observamos abaixo:

O código 4.1 corrigido deve apresentar-se como o código abaixo. A principal alteração acontece na linha 5 onde é construída a instrução SQL, na clausula WHERE além de filtrar por cliente a instrução filtra por usuário, ou seja, apenas o usuário previsto para aquele registro poderá realmente acessá-lo.

 1  
 2 <?php
 3 $idCliente = $_POST['idCliente'];
 4 $idUsuario = $usuario->getId();
 5 
 6 $sth = $dbh->prepare("SELECT * FROM clientes ".
 7                      "WHERE idCliente = :idCliente AND idUsuario = :idUsuario");
 8 $sth->bindParam(':idCliente', $idCliente);
 9 $sth->bindParam(':idUsuario', $idUsuario);
10 $sth->execute();
11 ?>

O código abaixo é a versão corrigida. A linha 2 cria um array com dois índices e armazena em $array_idiomas, trata-se do mapeamento ao objeto. A linha 4 recebe o dado via método post ou get e armazena em $idioma_suspeito. A linha 5 apenas inicializa a variável $idioma_seguro. A Linha 7 utiliza-se expressão regular para checar, através do método "white list" o parâmetro recebido. A linha 8 checa o parâmetro recebido com o "mapa" construído na linha 2 e em caso positivo concatena o valor de $idioma_suspeito com a string ".php"(linha 9). A linha 11 executa o código normalmente.

 1  
 2 <?php
 3 $array_idiomas   = array("en", "pt");
 4 
 5 $idioma_suspeito = $_REQUEST['idioma'];
 6 $idioma_seguro   = "";
 7 
 8 if( preg_match("/^[0-9]{1}$/", $idioma_suspeito) ){
 9     if(  in_array($idioma_suspeito, $array_idiomas)  ){
10         $idioma_seguro = $idioma_suspeito.".php";
11         
12         require $idioma;
13         # demais código
14         
15     }else{
16         # registrar possível tentativa de ataque
17     }
18 }else{
19     # registrar possível tentativa de ataque
20 }
21 ?>

Comentários

comments powered by Disqus