SQL Injection
Autor: Claudio Weiler | Publicado: 2016-06-01T09:44:00-03:00

Esta postagem vem bater em uma tecla repetitiva que possui material exaustivo na internet, inclusive nesta nossa língua nativa, e apesar de ser um dos conceitos de segurança mais importantes no desenvolvimento de aplicações com banco de dados, é incrivelmente ignorada no ensino da programação, seja em cursos, faculdades ou tutoriais e artigos na internet.

Navegando pela internet pode ser que você encontre algum tópico em fórum com uma resposta minha insistindo para que os participantes do fórum atentem para a falha de codificação permitindo SQL Injection, e isto pode acabar parecendo uma insistência desnecessária, abusiva ou até arrogante, mas, estamos falando em segurança.

O que é SQL Injection?
SQL Injection, ou Injeção de SQL, nada mais é do que utilizar a interface do usuário para alterar o comando SQL enviado ao banco de dados e obter resultados, entre outros, potencialmente danosos como: login com nível de administrador, alteração de dados do banco, ou até a exclusão de tabelas completas.

E como isto é feito?
Normalmente aprendemos a manipular banco de dados através de interface direta antes de integrar o uso de banco de dados no desenvolvimento de aplicações. A manipulação de banco de dados nada mais é que uma linha de comando representada por um texto que obedece algumas regras de formatação, por exemplo:
select * from usuarios where id = 'admin' and senha = 'supersenha';
O problema acontece quando o desenvolvedor precisa trazer este comando para a aplicação. A primeira assimilação que o desenvolvedor faz é a de que o comando não passa de um texto, ou, em linguagem, uma String, e imediatamente inicia a sua construção com uma concatenação de Strings com entradas do usuário:
String usuario = *obtido da interface*;
String senha =  *obtido da interface*;
String comandoSQL = "select * from usuarios where id = '" + usuario + "' and senha = '" + senha + "'";
Pronto, está feita a lambança! Agora basta que o usuário mal intencionado manipule as entradas da interface para corromper o comando SQL:
String usuario = "admin"; // obtido da interface
String senha =  "' or '1'='1"; // obtido da interface
String comandoSQL = "select * from usuarios where id = '" + usuario + "' and senha = '" + senha + "'";
// resultado concatenado: "select * from usuarios where id = 'admin' and senha = '' or '1'='1'"
Obs.: Estes exemplos estão baseado em Java.

Os exemplos acima estão vinculados a um formulário de login qualquer e representam apenas 1 das possibilidades de SQL Injection, onde o hacker assume a existência de um usuário com a id "admin". O hacker poderá repetir esta forma com vários outros ids normalmente usados para administradores, e apesar do nosso objetivo não seja entrar em outros conceitos de segurança, como não utilizar ids comuns, nós podemos assumir que se o desenvolvedor está permitindo o SQL Injection por desconhecimento, diversas outras falhas de segurança também existirão.

Não querendo ser extensivo, caso exista interesse em saber outras formas de alteração do comando SQL, uma rápida pesquisa pela internet trará várias respostas.

E como evitar?
Certo, já vimos como é possível fazer, mas e como podemos evitar este tipo de ataque?

Cada linguagem possui sua forma de sintaxe, e várias soluções são possíveis para evitar o SQL Injection. Uma solução genérica, que funciona em toda linguagem, é o tratamento da entrada feita pelo usuário antes de concatenar com o template do comando SQL, removendo ou escapando caracteres potencialmente nocivos. Mas esta solução é trabalhosa e pode trazer outros problemas à aplicação. A nossa recomendação é que se estude a forma adequada de tratamento para a linguagem específica que estiver utilizando, que normalmente será através de prepared statements, e é o exemplo que vamos ver abaixo para Java:
String usuario = *obtido da interface*;
String senha =  *obtido da interface*;
String comandoSQL = "select * from usuarios where id = ? and senha = ?";
PreparedStatement ps = conexao.prepareStatement(comandoSQL);
ps.setString(1, usuario);
ps.setString(2, senha);
ps.execute...
Observe, neste exemplo, que é passada para a conexão uma String com marcadores de lugar representados pelos caracteres de interrogação, e recebido um prepared statement, posteriormente são adicionadas as variáveis ao prepared statement que irá definir estes valores nos locais indicados de forma segura: o banco de dados interpreta e compila apenas o comando e depois utiliza os valores passados no comando já compilado, sem possibilidade de interpretação de comando ocultos.

Concluindo
Espero que esta postagem ajude na conscientização da necessidade de levar a segurança em consideração desde o início do aprendizado da programação, e que você leitor, seja um curioso, aprendiz, profissional da área, professor, ..., ajude na campanha por aplicativos mais seguros e confiáveis.