Evitando SQL Injection com PHP

(Last Updated On: 10 de outubro de 2016)

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;
	}

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:

  • no campo email: qualquercoisa@aleatoria.com
  • no campo senha: ' or id = '1

Nossa query completa ficaria:

SELECT * FROM Usuario Where Email = 'qualquercoisa@aleatoria.com'
AND Senha = '' or id ='1'

Ou seja, estamos selecionando tudo da tabela Usuario onde o email é igual a “qualquercoisa@aleatoria.com” 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 = 'qualquercoisa@aleatoria.com'
AND Senha = ' or id =1'

Ou seja, estamos procurando na tabela Usuario a instância de email “qualquercoisa@aleatoria.com” 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 carreira 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!

Fique por dentro

  • Thiago Lucas Teramae

    Bem legal, pontos importantes que passam despercebidos por muitos, e fazem a diferença!

    Aproveitando, precisa ajustar este link no post: “como acessar o banco de dados com PHP 7: mysqli ou PDO?” 🙂

    Abraços

    • andré Chaves

      Oi Thiago, tudo bom?

      Fico feliz que tenha gostado!

      Obrigado pelo feedback, já consertamos =)

Próximo ArtigoComunicação entre Apps Android com Intents