Injeção de SQL
(SQL Injection)
CWE-89: Improper Neutralization of
Special Elements used in an
SQL Command ('SQL Injection')
Injeção de SQL (SQL Injection)
• Agentes podem interferir nos comandos enviados ao banco
de dados pela aplicação, alterando a lógica de consultas para
obter dados que deveriam ser inacessíveis ou corromper o
banco de dados.
• Ocorre quando acessos ao banco de dados dependem da
entrada de dados pelo usuário.
• Quando a entrada de dados contém o caractere apóstrofo (‘),
a entrada do usuário pode ser interpretada como comando
SQL, ao invés de dados do usuário.
Injeção de SQL (SQL Injection)
Considerar uma aplicação que requer autenticação:
String sql = "SELECT * FROM USUARIOS" +
" WHERE LOGIN = '" + [Link]() +
"' AND PASSWORD = '" + [Link]() + "'";
try (Statement stmt = [Link]()) {
ResultSet rs = [Link](sql);
if (![Link]()) {
throw new UserInvalidException();
}
User user = new User();
[Link] = [Link]("login");
[Link] = [Link]("id");
return user;
} catch (SQLException e) {
[Link]();
}
Comando enviado SELECT * FROM USUARIOS
WHERE LOGIN = 'gilvanj@[Link]'
ao banco: AND PASSWORD = '1234567'
Injeção de SQL (SQL Injection)
Agora, considerar a seguinte entrada de dados
Sendo a instrução que cria o comando:
String sql = "SELECT * FROM USUARIOS" +
" WHERE LOGIN = '" + [Link]() +
"' AND PASSWORD = '" + [Link]() + "'";
Comando enviado ao banco:
SELECT * FROM USUARIOS
WHERE LOGIN = 'gilvanj@[Link]'; --'
AND PASSWORD = '1234567'
Ou seja, será executado:
SELECT * FROM USUARIOS
WHERE LOGIN = 'gilvanj@[Link]';
Injeção de SQL (SQL Injection)
Outra entrada possível:
Sendo a instrução que envia o comando:
String sql = "SELECT * FROM USUARIOS" +
" WHERE LOGIN = '" + [Link]() +
"' AND PASSWORD = '" + [Link]() + "'";
Esta entrada cria o seguinte comando SQL:
SELECT * FROM USUARIOS
WHERE LOGIN = 'a' or 1=1 --'
AND PASSWORD = '1234567'
Ou seja, será executado:
SELECT * FROM USUARIOS
WHERE LOGIN = 'a' or 1=1
Injeção de SQL (SQL Injection)
Considerar esta modelagem:
E uma aplicação para listar as folhas de pagamento do usuário corrente:
Sendo este o comando para listar a folha de pagamento:
String comando = "select * from folha_pagamento" +
" where funcionario_id = " +
getCurrentUser().getFuncionario().getId() +
" and competencia = '01/" + data + "'";
Esta entrada cria o seguinte comando SQL:
select * from folha_pagamento where funcionario_id = 2 and
competencia = '01/10/2021'
Injeção de SQL (SQL Injection)
Com esta entrada:
String comando = "select valor_bruto, valor_descontos" +
" from folha_pagamento" +
" where funcionario_id = " +
getCurrentUser().getFuncionario().getId() +
" and competencia = '01/" + data + "'";
10/2021’ or 1=1 --
Seria executado:
select valor_bruto, valor_descontos from folha_pagamento where funcionario_id = 2
and competencia = '01/10/2021' or 1=1--'
Injeção de SQL (SQL Injection)
Considerar esta entrada de dados:
a’; DELETE FROM USUARIOS; --
Esta entrada cria o seguinte comando SQL:
SELECT * FROM USUARIOS
WHERE LOGIN = 'a'; DELETE FROM USUARIOS; --'
AND PASSWORD = ''
Isto é, são executados estes comandos:
SELECT * FROM USUARIOS WHERE LOGIN = 'a';
DELETE FROM USUARIOS;
Injeção de SQL (SQL Injection)
Numa aplicação web, se a pilha de execução for retornada para o frontend, o
agente pode se aproveitar deste comportamento.
String comando = "select valor_bruto, valor_descontos" +
" from folha_pagamento" +
" where funcionario_id = " +
getCurrentUser().getFuncionario().getId() +
" and competencia = '01/" + data + "'";
Injeção de SQL (SQL Injection)
Foi acrescentado group by valor_bruto
(having 1=1 não precisa mais manter)
Injeção de SQL (SQL Injection)
Uma vez que se conhece a estrutura da tabela, o agente pode
alterar estruturas do banco de dados:
A API em Java aplica a alteração no
banco de dados, mas lança uma
; update folha_pagamento set valor_bruto = 8888 -- exceção.
[Link]: ResultSets múltiplos foram retornados pela consulta.
at [Link]([Link])
at [Link]([Link])
at [Link]([Link])
at [Link]([Link])
at [Link]$0([Link])
at [Link]/[Link]([Link])
at [Link]/[Link]$[Link]([Link])
at [Link]/[Link]([Link])
Injeção de SQL (SQL Injection)
O agente pode também tentar utilizar comandos DDL (Data
definition language) para comprometer o banco de dados.
; drop table folha_pagamento; --
A API em Java aplica a alteração
no banco de dados, embora
lance uma exceção.
Injeção de SQL (SQL Injection)
• Um web site que permite listar dados de um pedido executa:
String comando = "SELECT * FROM PEDIDO WHERE ID = " +
[Link]("id");
ResultSet rs = [Link](comando);
• Exemplo de chamada esperada:
[Link]
• Ao chamar o web site poderia ser informado:
[Link] DATABASE DB
• O comando que será executado é:
SELECT * FROM PEDIDO WHERE ID = 15362;
DROP DATABASE DB
Injeção de SQL
• Exibir a pilha de execução para o usuário final pode introduzir um risco de
segurança em potencial.
• Por isso, é uma boa prática de desenvolvimento de sistema seguro:
Nunca expor detalhes do erro, tal como a pilha de
execução, aos usuários finais
• A pilha de execução deveria ser apresentada apenas em arquivos de log.
– Apresentar apenas mensagens amigáveis ao usuário final
• Esta vulnerabilidade está documentada no CWE-209 (Generation of Error
Message Containing Sensitive Information)
Injeção de SQL (SQL Injection)
• Os impactos com a injeção com SQL podem ser:
– Confidencialidade – Possibilita a leitura de dados que o usuário não
deveria ter acesso
– Integridade – É possível fazer alterações ou exclusão de informações
– Disponibilidade – Ao corromper estruturas de dados que
impossibilitem aos demais usuários de utilizar a aplicação
Injeção de SQL (SQL Injection)
O SGBD da Microsoft possui stored procedures que dão acesso a
funcionalidades fora do banco de dados:
• XP_CMDSHELL – Executa um comando da linha de comando
• XP_REGREAD – Lê chaves do registro do Windows
• XP_SERVICECONTROL – Acesso aos serviços
Recomendações para evitar injeção de SQL
• Limitar as entradas de dados,
– Através de lista branca: para impedir caracteres especiais
• Aceitar apenas os caracteres considerados válidos, recusando os demais
caracteres
• O usuário de banco de dados da aplicação deve possuir
privilégios reduzidos e que impeçam a execução de comandos
DDL .
• Utilizar comandos SQL parametrizados.
Parametrizar comandos SQL
Acesso sem usar comando parametrizado:
String sql = "SELECT * FROM USUARIOS" +
" WHERE LOGIN = '" + [Link]() +
"' AND PASSWORD = '" + [Link]() + "'";
try (Statement stmt = [Link]()) {
ResultSet rs = [Link](sql);
if ([Link]()) {
return true;
}
} catch (SQLException e) {
[Link]();
}
Acesso usando comando parametrizado:
String sql = "SELECT * FROM USUARIOS WHERE LOGIN = ? AND PASSWORD = ?";
try (PreparedStatement stmt = [Link](sql)) {
[Link](1, [Link]());
[Link](2, [Link]());
ResultSet rs = [Link]();
if ([Link]()) {
return true;
}
} catch (SQLException e) {
[Link]();
}
Parametrizar comandos SQL
Ao utilizar pesquisas parametrizadas, primeiro o comando é
enviado ao banco (sem valores absolutos de parâmetros).
"SELECT * FROM USUARIOS WHERE LOGIN = ? AND PASSWORD = ?"
gilvanj@[Link]’;-- (string vazia)
Injeção de SQL (SQL Injection)
• Comandos que utilizam o operador like podem receber
injeção de caracteres curingas (%, _).
• Em 2011, a prática de injeção de SQL foi responsável por
comprometer sistemas de organizações como Sony Pictures,
PBS, [Link] entre outras.