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:
- Escolher uma extensão para conectar com o banco de dados (Se você não sabe qual usar, dê uma olhada no meu post sobre como acessar o banco de dados com PHP 7: mysqli ou PDO?)
- Escrever uma query buscando pelo email e senha
- Executar a query
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: [email protected]
no campo senha:
' or id = '1
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!