Em relação a muitas respostas úteis, espero acrescentar algum valor a este tópico.
A injeção de SQL é um ataque que pode ser feito por meio de entradas do usuário (entradas que são preenchidas por um usuário e usadas em consultas). Os padrões de injeção SQL são sintaxe de consulta correta, enquanto podemos chamá-lo: consultas incorretas por motivos inadequados, e assumimos que pode haver uma pessoa má que tenta obter informações secretas (ignorando o controle de acesso) que afetam os três princípios de segurança (confidencialidade). , integridade e disponibilidade).
Agora, nosso objetivo é evitar ameaças à segurança, como ataques de injeção SQL, a pergunta (como evitar um ataque de injeção SQL usando PHP), seja mais realista: filtragem de dados ou limpeza de dados de entrada é o caso ao usar dados de entrada de usuários dentro essa consulta, usando PHP ou qualquer outra linguagem de programação não é o caso, ou conforme recomendado por mais pessoas para usar tecnologia moderna, como declaração preparada ou qualquer outra ferramenta que atualmente suporte a prevenção de injeção de SQL, considera que essas ferramentas não estão mais disponíveis? Como você protege seu aplicativo?
Minha abordagem contra a injeção de SQL é: limpar os dados de entrada do usuário antes de enviá-los ao banco de dados (antes de usá-los em qualquer consulta).
Filtragem de dados para (conversão de dados não seguros em dados seguros)
Considere que o PDO e o MySQLi não estão disponíveis. Como você pode proteger seu aplicativo? Você me força a usá-los? E outras línguas além do PHP? Prefiro fornecer idéias gerais, pois podem ser usadas para bordas mais amplas, não apenas para um idioma específico.
- Usuário SQL (limitando o privilégio do usuário): as operações SQL mais comuns são (SELECT, UPDATE, INSERT); por que atribuir o privilégio UPDATE a um usuário que não o exige? Por exemplo, as páginas de login e pesquisa estão usando apenas SELECT; por que usar usuários de banco de dados nessas páginas com altos privilégios?
REGRA: não crie um usuário de banco de dados para todos os privilégios. Para todas as operações SQL, você pode criar seu esquema como (deluser, selectuser, updateuser) como nomes de usuário para facilitar o uso.
Veja o princípio do menor privilégio .
Filtragem de dados: antes de criar qualquer entrada do usuário da consulta, ela deve ser validada e filtrada. Para programadores, é importante definir algumas propriedades para cada variável de entrada do usuário:
tipo de dados, padrão de dados e comprimento dos dados . Um campo que é um número entre (x e y) deve ser exatamente validado usando a regra exata, e para um campo que é uma sequência (texto): padrão é o caso, por exemplo, um nome de usuário deve conter apenas alguns caracteres, vamos diga [a-zA-Z0-9_-.]. O comprimento varia entre (x e n) onde x e n (números inteiros, x <= n).
Regra: criar filtros exatos e regras de validação são práticas recomendadas para mim.
Use outras ferramentas: Aqui, também concordarei com você que uma declaração preparada (consulta parametrizada) e procedimentos armazenados. As desvantagens aqui são que essas maneiras exigem habilidades avançadas que não existem para a maioria dos usuários. A idéia básica aqui é distinguir entre a consulta SQL e os dados usados dentro. Ambas as abordagens podem ser usadas mesmo com dados não seguros, porque os dados de entrada do usuário aqui não adicionam nada à consulta original, como (any ou x = x).
Para obter mais informações, leia o OWASP SQL Injection Prevention Cheat Sheet .
Agora, se você é um usuário avançado, comece a usar essa defesa como quiser, mas, para iniciantes, se eles não puderem implementar rapidamente um procedimento armazenado e prepararem a instrução, é melhor filtrar os dados de entrada o máximo possível.
Por fim, vamos considerar que um usuário envia este texto abaixo em vez de digitar seu nome de usuário:
[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'
Essa entrada pode ser verificada antecipadamente, sem qualquer declaração preparada e procedimentos armazenados, mas, por segurança, o uso deles é iniciado após a filtragem e validação dos dados do usuário.
O último ponto é detectar comportamentos inesperados, que exigem mais esforço e complexidade; não é recomendado para aplicativos da web normais.
O comportamento inesperado na entrada do usuário acima é SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA e root. Depois que essas palavras forem detectadas, você poderá evitar a entrada.
ATUALIZAÇÃO 1:
Um usuário comentou que esta postagem é inútil, OK! Aqui está o que o OWASP.ORG forneceu :
Defesas primárias:
Opção nº 1: Uso de instruções preparadas (consultas parametrizadas)
Opção nº 2: Uso de procedimentos armazenados
Opção nº 3: Escapando todas as entradas fornecidas pelo usuário
Defesas adicionais:
Aplicar também: Menor privilégio
Executar também: Validação de entrada da lista branca
Como você deve saber, a reivindicação de um artigo deve ser apoiada por um argumento válido, pelo menos por uma referência! Caso contrário, é considerado um ataque e uma reivindicação ruim!
Atualização 2:
No manual do PHP, PHP: Instruções Preparadas - Manual :
Escapamento e injeção de SQL
Variáveis vinculadas serão escapadas automaticamente pelo servidor. O servidor insere seus valores de escape nos locais apropriados no modelo de instrução antes da execução. Uma dica deve ser fornecida ao servidor para o tipo de variável vinculada, para criar uma conversão apropriada. Veja a função mysqli_stmt_bind_param () para mais informações.
O escape automático de valores no servidor às vezes é considerado um recurso de segurança para impedir a injeção de SQL. O mesmo grau de segurança pode ser alcançado com instruções não preparadas se os valores de entrada forem escapados corretamente.
Atualização 3:
Criei casos de teste para saber como o PDO e o MySQLi enviam a consulta ao servidor MySQL ao usar uma instrução preparada:
DOP:
$user = "''1''"; // Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));
Log de consulta:
189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
189 Quit
MySQLi:
$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();
Log de consulta:
188 Prepare SELECT * FROM awa_user WHERE username =?
188 Execute SELECT * FROM awa_user WHERE username ='\'\'1\'\''
188 Quit
É claro que uma declaração preparada também está escapando dos dados, nada mais.
Como também mencionado na declaração acima,
O escape automático de valores no servidor às vezes é considerado um recurso de segurança para impedir a injeção de SQL. O mesmo grau de segurança pode ser alcançado com instruções não preparadas, se os valores de entrada forem escapados corretamente
Portanto, isso prova que a validação de dados, como intval()
é uma boa idéia para valores inteiros, antes de enviar qualquer consulta. Além disso, impedir dados maliciosos do usuário antes de enviar a consulta é uma abordagem correta e válida .
Por favor, veja esta pergunta para mais detalhes: O PDO envia uma consulta bruta ao MySQL enquanto o Mysqli envia uma consulta preparada, ambas produzem o mesmo resultado
Referências:
- Folha de dicas sobre injeção de SQL
- Injeção SQL
- Segurança da informação
- Princípios de segurança
- Data de validade