Evitando SQL Injection com PHP

Evitando SQL Injection com PHP

Quando desenvolvemos nossos sistemas, é normal que a gente faça pensando no usuario final. Portanto, ao criar um formulário de login que verifica um email e senha no banco de dados já temos em mente o que precisamos fazer no back-end:

Neste post vamos trabalhar com PDO. Com essa extensão, teríamos algo como:


public function login($email,$senha) {

$query = "SELECT * FROM Usuario WHERE Email = '". $email. "' AND Senha = '". $senha."'";

$result = $this->con->query($query); 
$usuario = $result->fetchObject('Vendor Model Usuario');
return $usuario;
}
Banner da Escola de Programação: Matricula-se na escola de Programação. Junte-se a uma comunidade de mais de 500 mil estudantes. Na Alura você tem acesso a todos os cursos em uma única assinatura; tem novos lançamentos a cada semana; desafios práticos. Clique e saiba mais!

Beleza, assim conseguimos verificar o email e a senha. Para um usuário comum, nosso método funciona perfeitamente!

Mas, um usuário malicioso, com um pouco de conhecimento, pode tentar passar códigos SQL nos inputs do form, como por exemplo:

Nossa query completa ficaria:

SELECT * FROM Usuario Where Email = '[email protected]' AND Senha = '' or id ='1'

Ou seja, estamos selecionando tudo da tabela Usuario onde o email é igual a "[email protected]" e senha é vazia OU o id igual a 1.

A setença OR ID = 1 será verdadeira se houver o registro no banco. Como os valores de id, normalmente, são auto incrementados isso fará com que o usuário malicioso entre no nosso sistema como o usuario de id = 1.

Mudar a query desta forma é o que chamamos de SQL Injection. Existem diversas formas de lidar com isso, a mais comum é usar prepared statement, uma forma de escrever queries passando atributos ao invés de concatenar as variáveis com a string.

Com PDO, nosso prepared statement ficará:


public function login($email,$senha) { 

$query = "SELECT \* FROM Usuario WHERE Email = :email AND Senha = :senha";

$statement = $this->con->prepare($query);
$statement->bindValue(":email",$email); 
$statement->bindValue(":senha",$senha);

$statement->execute(); 
$usuario = $statement ->fetchObject('Vendor Model Usuario'); 
return $usuario; 
}

Ao invés de usar a função query da conexão, podemos usar a função ->prepare() e então passar os valores de Email e Senha pelo método ->bindValue() que realizará uma verificação do conteudo das variáveis $email e $senha evitando os casos mais comuns de SQL Injection.

Somente após, associamos aos atributos :email e :senha da query e, por fim, é executada a query!

Além disso, aumentamos a semantica já que usamos parametros na query ao invés de concatenar os valores das variáveis $email e $senha na string $query.

Nesse momento, nossa query está algo como:

SELECT * FROM Usuario Where Email = '[email protected]' AND Senha = ' or id =1'

Ou seja, estamos procurando na tabela Usuario a instância de email "[email protected]" e senha "or id =1". Logo, a senha não será validada e nosso usuario malicioso não entrará em nosso sistema!

É extremamente importante pensarmos em todos os tipos de usuario quando desenvolvemos nossos sistemas. Por isso, temos que estar sempre atentos ao desenvolver nossas queries e nos proteger contra SQL Injection.

E você, o que achou de SQL Injection? Gostou de prepared statements? Compartilhe sua opinião com a gente!

Quer aprender mais sobre PHP? Conheça a formação desenvolvedor PHP junior aqui da alura! Ou então, que tal ir além e conhecer mais a fundo a extensão PDO, suas vantagens e desvatagens? Faça o curso de PDO com a gente aqui na Alura!

André Chaves
André Chaves

Instrutor na Caelum e líder técnico na Hefesto Software House. No tempo livre, escritor no blog https://medium.com/code-maestro. Twitter @andrechavesg

Veja outros artigos sobre Programação