Como evitar um ATAQUE DE INJEÇÃO DE SQL
30/03/2022
Por Graciela Martínez y Guillermo Pereyra – LACNIC CSIRT
O ataque de injeção de SQL é uma técnica de hacking amplamente usada. Conhecido como SQLi, é um ataque em que uma ou mais instruções SQL válidas são “injetadas” na consulta de um aplicativo a um banco de dados.
Structured Query Language, conhecida por suas siglas em inglês como a SQL, é uma linguagem de comando e controle para gerenciar bancos de dados relacionais.
Como acontece um ataque de injeção de SQL?
O ataque é alcançado quando um aplicativo aceita dados de fontes não confiáveis- que foram alterados para serem interpretados como código- e também não realiza uma validação correta dos mesmos antes de usá-los para realizar uma consulta dinâmica ao banco de dados.
Vetores de ataque
Ao projetar um aplicativo, é importante levar em conta que qualquer entrada de dados por parte de um usuário é suscetível de ser modificada arbitrariamente.
Normalmente, os ataques a aplicativos web são realizados modificando parâmetros na URL, usando o método HTTP GET ou, como veremos nos exemplos, modificando qualquer tipo de entrada de dados com o método HTTP POST. Dependendo de como o aplicativo web seja projetado, é possível explorar essa vulnerabilidade modificando cabeçalhos HTTP como: User Agent, cookies, referrer ou cabeçalhos próprios do sistema.
Outros vetores possíveis de ataque devem ser considerados, tais como o leitor de barras, leitor de QR ou câmera de vídeo que reconheça texto.
É importante observar que um atacante pode ser externo ou pode pertencer à organização afetada.
Quais são as possíveis consequências de um ataque de injeção de SQL?
Qualquer ação não desejada executada em um banco de dados como resultado de um ataque SQLi pode afetar um ou vários pilares de segurança da informação.
Um ataque bem-sucedido pode permitir que o atacante concretize várias ações que afetam a confidencialidade, integridade e/ou disponibilidade das informações.
Exemplos:
- A confidencialidade pode ser afetada em caso de acesso a informações sensíveis sem a autorização necessária.
- A integridade pode ser afetada se as informações forem removidas ou modificadas sem a autorização necessária.
- A disponibilidade pode ser afetada se as informações não estiverem disponíveis no momento em que são necessárias, seja porque não podem ser acessadas ou porque foram alteradas previamente sem autorização.
Outros problemas que um ataque SQLi poderia explorar são falhas na autenticação e/ou modificação da autorização que um perfil de usuário tem para realizar determinadas ações sobre um recurso. Por exemplo, se as informações de um banco de dados onde as senhas de usuários são armazenadas fossem acessadas, se estas fossem ofuscadas com hashes criptográficos robustos, o acesso impróprio a elas e seu possível uso por parte de um atacante seriam evitados.
Portanto, um ataque de SQLi bem-sucedido afeta vários pilares ao mesmo tempo.
Como evitar um ataque de injeção de SQL?
Existem diferentes tipos de medidas que devem ser consideradas quando um sistema precisa se comunicar com um banco de dados relacional por meio do uso de SQL.
A seguir, algumas medidas básicas que devem ser implementadas:
- Uso de consultas parametrizadas: Prepared Statements.
- Uso de procedimentos: Stored Procedures.
- Validação de entrada de dados de usuários.
- Escapar todas as entradas permitidas dos usuários.
Uso de consultas parametrizadas: Prepared Statements.
Preparar as declarações SQL e armazená-las em uma variável antes da execução é uma maneira simples e segura de programar. Fazê-lo com antecedência evita que um atacante insira declarações no nosso banco de dados, como pode se ver no segundo exemplo.
As linguagens mais usadas têm métodos para parametrizar com segurança as declarações que precisam dados de entrada de um usuário. Algumas dessas linguagens são:
- Java EE – usar PreparedStatement() nos nossos parâmetros dinâmicos (variável Bind em inglês)
- .NET – usar consultas parametrizadas como SqlCommand() ou OleDbCommand() nos nossos parâmetros dinâmicos.
- PHP – é possível usar PDO para bancos de dados genéricos com uma forte parametrização das consultas ou no caso de usar um driver específico para um banco de dados é necessário procurar uma função segura para preparar nossa declaração, por exemplo, para MySQL é necessário usar bind_param().
Uso de procedimentos: Stored Procedures.
Ao executar procedimentos diretamente no banco de dados, deve-se tomar cuidado para não incluir nenhuma geração de declaração SQL dinâmica não segura. Esses procedimentos devem ter validação das entradas e um “escape” adequado das mesmas.
Validação de entrada de dados de usuários.
Em todos os casos, e não apenas para evitar injeções de SQL, é necessária a validação correta da entrada de dados por parte dos usuários.
Recomenda-se não usar variáveis dinâmicas para nomes de tabelas ou colunas, nem para indicar a ordem de classificação (ASC ou DESC). Se necessário, deverão ser usadas validações anteriores, como converter as entradas para variáveis booleanas ou usar funções SWITCH, Sort of, etc.
Escapar todas as entradas permitidas dos usuários: Escape characters.
Escapar entradas de usuários para convertê-las em outro formato como strings, deve ser usado com cuidado, pois não impede todas as injeções. Essa técnica de escape characters depende de cada mecanismo de banco de dados, por isso é importante implementar um controle que impeça um possível desvio desta medida.
Exemplos:
A seguir, alguns exemplos de códigos que permitiriam ataques de SQLi.
Exemplo 1: Separar resultados em páginas, usando PHP e Postgres.
<?php
$índice = $argv[0];
$consulta = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $índice;";
$resultado = pg_query($conexión, $consulta);
?>
Um usuário normalmente usa os botões “próximo” e “anterior” para navegar entre os resultados, o que alteraria o valor decimal da variável “índice” na URL.
Esse comportamento não causaria problemas, mas se um agente mal-intencionado decidir adicionar o seguinte código à URL:
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'crack', usesysid, 't','t','crack'
from pg_shadow where usename='postgres';
--
Então a variável $consulta ficaria da seguinte forma:
$consulta = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET 0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'superusuario', usesysid, 't','t','password'
from pg_shadow where usename='postgres';
--;"
E, como resultado, estaria criando o usuário “superusuário” com privilégios, portanto, estaria habilitado a realizar atividades maliciosas como as descritas anteriormente no artigo.
Uma forma de corrigir este problema seria o uso do PDO (PHP Data Objects):
<?php
$stmt = $pdo->prepare("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET :índice;");
$índice = $argv[0];
$stmt->bindParam(':indice', $indice);
$stmt->execute()
?>
Exemplo 2: Bypass de autenticação
Suponhamos um aplicativo web que tem um formulário de autenticação que aceita um nome de usuário e uma senha como entradas.
Esse formulário está sendo processado por um código que contém a seguinte declaração SQL:
consulta = "SELECT * FROM users WHERE username = "'" + username + "' AND password = '" + password + "'"
Como se pode apreciar, a consulta ao banco de dados é construída por meio do uso de instruções SQL e os valores inseridos pelo usuário são designados às variáveis de forma direta.
Neste caso, um usuário mal-intencionado poderia entrar como usuário: admin e como senha pass’ OR ‘1’=’1.
A consulta ao banco de dados final seria a seguinte:
consulta = SELECT * FROM users WHERE username = 'admin' AND (password = ' pass' OR '1'='1 ')
Essa consulta buscaria todos os dados associados ao usuário privilegiado ‘admin’ porque a condição booleana sempre será verdadeira.
Conclusão
Um ataque de injeção de SQL é evitável se os controles necessários forem implementados durante o desenvolvimento do aplicativo.
As equipes de desenvolvedores devem ter um procedimento que inclua boas práticas para um desenvolvimento de software seguro, que inclua um tempo prudente para o estágio de teste antes de passar para a produção.
Referências:
https://blog.sucuri.net/2022/01/understanding-website-sql-injections.html
https://owasp.org/Top10/A03_2021-Injection/
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
https://www.esecurityplanet.com/threats/how-to-prevent-sql-injection-attacks/
https://www.php.net/manual/es/security.database.sql-injection.php
https://www.esecurityplanet.com/threats/how-to-prevent-sql-injection-attacks/
As opiniões expressas pelos autores deste blog são próprias e não refletem necessariamente as opiniões de LACNIC.