Use instruções preparadas e consultas parametrizadas. Estas são instruções SQL enviadas e analisadas pelo servidor de banco de dados separadamente de quaisquer parâmetros. Dessa forma, é impossível para um invasor injetar SQL malicioso.
Você basicamente tem duas opções para conseguir isso:
Usando o PDO (para qualquer driver de banco de dados suportado):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// Do something with $row
}
Usando o MySQLi (para MySQL):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// Do something with $row
}
Se você estiver se conectando a um banco de dados que não seja o MySQL, existe uma segunda opção específica do driver à qual você pode se referir (por exemplo, pg_prepare()
e pg_execute()
no PostgreSQL). O DOP é a opção universal.
Configurando corretamente a conexão
Observe que ao usar PDO
para acessar um banco de dados MySQL, as instruções reais preparadas não são usadas por padrão . Para corrigir isso, você deve desativar a emulação de instruções preparadas. Um exemplo de criação de uma conexão usando o PDO é:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
No exemplo acima, o modo de erro não é estritamente necessário, mas é recomendável adicioná-lo . Dessa forma, o script não irá parar com um Fatal Error
quando algo der errado. E isso dá ao desenvolvedor a chance de catch
qualquer erro que seja throw
n como PDOException
s.
O que é obrigatório , no entanto, é a primeira setAttribute()
linha, que informa ao PDO para desativar as instruções preparadas emuladas e usar as declarações reais preparadas. Isso garante que a instrução e os valores não sejam analisados pelo PHP antes de enviá-la ao servidor MySQL (dando a um possível invasor nenhuma chance de injetar SQL malicioso).
Embora você possa definir as charset
opções do construtor, é importante observar que as versões 'mais antigas' do PHP (anteriores à 5.3.6) ignoravam silenciosamente o parâmetro charset no DSN.
Explicação
A instrução SQL para a qual você passa prepare
é analisada e compilada pelo servidor de banco de dados. Ao especificar parâmetros (um ?
ou um parâmetro nomeado, como :name
no exemplo acima), você informa ao mecanismo do banco de dados onde deseja filtrar. Então, quando você chama execute
, a instrução preparada é combinada com os valores dos parâmetros especificados.
O importante aqui é que os valores dos parâmetros são combinados com a instrução compilada, não uma string SQL. A injeção de SQL funciona enganando o script para incluir seqüências maliciosas quando ele cria o SQL para enviar ao banco de dados. Portanto, enviando o SQL real separadamente dos parâmetros, você limita o risco de acabar com algo que não pretendia.
Quaisquer parâmetros que você enviar ao usar uma instrução preparada serão tratados apenas como strings (embora o mecanismo do banco de dados possa fazer alguma otimização, portanto os parâmetros também podem acabar como números, é claro). No exemplo acima, se a $name
variável contiver 'Sarah'; DELETE FROM employees
o resultado, seria simplesmente uma pesquisa para a sequência "'Sarah'; DELETE FROM employees"
e você não terminará com uma tabela vazia .
Outro benefício do uso de instruções preparadas é que, se você executar a mesma instrução várias vezes na mesma sessão, ela será analisada e compilada apenas uma vez, fornecendo alguns ganhos de velocidade.
Ah, e desde que você perguntou sobre como fazer isso para uma inserção, aqui está um exemplo (usando o DOP):
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
Instruções preparadas podem ser usadas para consultas dinâmicas?
Embora você ainda possa usar instruções preparadas para os parâmetros da consulta, a estrutura da consulta dinâmica em si não pode ser parametrizada e certos recursos da consulta não podem ser parametrizados.
Para esses cenários específicos, a melhor coisa a fazer é usar um filtro de lista de permissões que restrinja os valores possíveis.
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}
Se você estiver usando uma versão recente do PHP, a
mysql_real_escape_string
opção descrita abaixo não estará mais disponível (emboramysqli::escape_string
seja um equivalente moderno). Atualmente, amysql_real_escape_string
opção só faria sentido para o código legado em uma versão antiga do PHP.Você tem duas opções: escapar dos caracteres especiais no seu
unsafe_variable
ou usar uma consulta parametrizada. Ambos o protegeriam da injeção de SQL. A consulta parametrizada é considerada a melhor prática, mas exigirá a alteração para uma extensão mais recente do MySQL no PHP antes que você possa usá-la.Abordaremos a cadeia de menor impacto escapando primeiro.
Veja também, os detalhes da
mysql_real_escape_string
função.Para usar a consulta parametrizada, você precisa usar o MySQLi em vez das funções do MySQL . Para reescrever seu exemplo, precisaríamos de algo como o seguinte.
A principal função que você deseja ler seria
mysqli::prepare
.Além disso, como outros sugeriram, você pode achar útil / mais fácil aumentar uma camada de abstração com algo como DOP .
Observe que o caso que você perguntou é bastante simples e que casos mais complexos podem exigir abordagens mais complexas. Em particular:
mysql_real_escape_string
. Nesse tipo de caso, seria melhor passar a entrada do usuário por uma lista de permissões para garantir que apenas valores "seguros" sejam permitidos.mysql_real_escape_string
abordagem, sofrerá com o problema descrito por Polynomial nos comentários abaixo. Esse caso é mais complicado, pois os números inteiros não seriam cercados por aspas; portanto, você pode lidar com isso validando que a entrada do usuário contém apenas dígitos.fonte
mysql_real_escape_string
é suficiente ou devo usar parametrizado também?htmlentities
, por exemplomysql_real_escape_string()
integridade, mas não sou fã de listar primeiro a abordagem mais suscetível a erros. O leitor pode pegar rapidamente o primeiro exemplo. Ainda bem que ele está obsoleta agora :)mysql_*
funções estão obsoletas. Eles foram substituídos por funções semelhantesmysqli_*
, comomysqli_real_escape_string
.Todas as respostas aqui abrangem apenas parte do problema. De fato, existem quatro partes de consulta diferentes que podemos adicionar dinamicamente ao SQL:
E as declarações preparadas abrangem apenas dois deles.
Mas, às vezes, precisamos tornar nossa consulta ainda mais dinâmica, adicionando operadores ou identificadores também. Portanto, precisaremos de diferentes técnicas de proteção.
Em geral, essa abordagem de proteção é baseada na lista de permissões .
Nesse caso, todos os parâmetros dinâmicos devem ser codificados no seu script e escolhidos a partir desse conjunto. Por exemplo, para fazer pedidos dinâmicos:
Para facilitar o processo, escrevi uma função auxiliar da lista de permissões que faz todo o trabalho em uma linha:
Existe outra maneira de proteger identificadores - escapar, mas prefiro a lista de permissões como uma abordagem mais robusta e explícita. No entanto, desde que você tenha um identificador citado, você pode escapar do caractere de citação para torná-lo seguro. Por exemplo, por padrão para o mysql, você deve duplicar o caractere de citação para escapar dele . Para outras outras regras de escape do DBMS, seria diferente.
Ainda assim, há um problema com palavras-chave de sintaxe SQL (como
AND
,DESC
e tal), mas a lista branca parece ser a única abordagem nesse caso.Portanto, uma recomendação geral pode ser formulada como
Atualizar
Embora exista um acordo geral sobre as práticas recomendadas em relação à proteção contra injeção de SQL, ainda existem muitas práticas inadequadas. E alguns deles estão profundamente enraizados nas mentes dos usuários de PHP. Por exemplo, nesta página, existem (embora invisíveis para a maioria dos visitantes) mais de 80 respostas excluídas - todas removidas pela comunidade devido à má qualidade ou à promoção de práticas ruins e desatualizadas. Pior ainda, algumas das respostas ruins não são excluídas, mas sim prósperas.
Por exemplo, (1) existem (2) ainda (3) muitas (4) respostas (5) , incluindo a segunda resposta mais votada, sugerindo que você escape manualmente de strings - uma abordagem desatualizada que é comprovadamente insegura.
Ou há uma resposta um pouco melhor que sugere apenas outro método de formatação de string e até se orgulha de ser a panacéia definitiva. Embora, claro, não seja. Esse método não é melhor do que a formatação regular de strings, mas mantém todos os seus inconvenientes: é aplicável apenas a strings e, como qualquer outra formatação manual, é essencialmente uma medida opcional e não obrigatória, propensa a erros humanos de qualquer espécie.
Eu acho que tudo isso por causa de uma superstição muito antiga, apoiada por autoridades como o OWASP ou o manual do PHP , que proclama a igualdade entre qualquer "escape" e proteção contra injeções de SQL.
Independentemente do que o manual do PHP disser há anos,
*_escape_string
de maneira alguma torna os dados seguros e nunca foram planejados. Além de ser inútil para qualquer parte do SQL que não seja uma string, o escape manual está errado, porque é manual e oposto ao automatizado.E o OWASP piora ainda mais, enfatizando a fuga da entrada do usuário, o que é um absurdo absoluto: não deve haver tais palavras no contexto da proteção contra injeção. Toda variável é potencialmente perigosa - não importa a fonte! Ou, em outras palavras - todas as variáveis precisam ser formatadas corretamente para serem colocadas em uma consulta - não importa a fonte novamente. É o destino que importa. No momento em que um desenvolvedor começa a separar as ovelhas das cabras (pensando se alguma variável em particular é "segura" ou não), ele / ela dá seu primeiro passo em direção ao desastre. Sem mencionar que mesmo o texto sugere uma fuga em massa no ponto de entrada, semelhante ao recurso de citações mágicas - já desprezado, reprovado e removido.
Portanto, diferente de qualquer "escape", as instruções preparadas são a medida que realmente protege da injeção de SQL (quando aplicável).
fonte
Eu recomendo usar o PDO (PHP Data Objects) para executar consultas SQL parametrizadas.
Isso não apenas protege contra a injeção de SQL, mas também acelera as consultas.
E, usando o PDO em vez de
mysql_
,mysqli_
epgsql_
funções, você torna seu aplicativo um pouco mais abstrato do banco de dados, na rara ocorrência de que você precisa trocar de provedor de banco de dados.fonte
Use
PDO
e prepare consultas.(
$conn
é umPDO
objeto)fonte
Como você pode ver, as pessoas sugerem que você use no máximo declarações preparadas. Não está errado, mas quando sua consulta é executada apenas uma vez por processo, haverá uma pequena penalidade no desempenho.
Eu estava enfrentando esse problema, mas acho que o resolvi de maneira muito sofisticada - a maneira como os hackers usam para evitar o uso de aspas. Eu usei isso em conjunto com instruções preparadas emuladas. Eu o uso para evitar todos os tipos de possíveis ataques de injeção de SQL.
Minha abordagem:
Se você espera que a entrada seja inteira, verifique se ela é realmente inteira. Em uma linguagem de tipo variável como PHP, isso é muito importante. Você pode usar, por exemplo, esta solução muito simples, mas poderosa:
sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
Se você espera mais alguma coisa do número inteiro, hexa-o . Se você o enfeitiçar, você escapará perfeitamente de todas as entradas. No C / C ++, há uma função chamada
mysql_hex_string()
, no PHP você pode usarbin2hex()
.Não se preocupe com o fato de a string de escape ter um tamanho 2x do seu comprimento original, porque mesmo se você usar
mysql_real_escape_string
, o PHP precisará alocar a mesma capacidade((2*input_length)+1)
, que é a mesma.Esse método hexadecimal é frequentemente usado quando você transfere dados binários, mas não vejo razão para não usá-lo em todos os dados para impedir ataques de injeção de SQL. Observe que você precisa acrescentar dados previamente
0x
ou usar a função MySQLUNHEX
.Então, por exemplo, a consulta:
Se tornará:
ou
Hex é a fuga perfeita. Não há como injetar.
Diferença entre a função UNHEX e o prefixo 0x
Houve alguma discussão nos comentários, então eu finalmente quero deixar claro. Essas duas abordagens são muito semelhantes, mas são um pouco diferentes em alguns aspectos:
O ** ** 0x prefixo só pode ser utilizado para as colunas de dados, tais como CHAR, VARCHAR, texto, bloco, binário, etc .
Além disso, seu uso é um pouco complicado se você estiver prestes a inserir uma string vazia. Você precisará substituí-lo totalmente
''
ou receberá um erro.UNHEX () funciona em qualquer coluna; você não precisa se preocupar com a string vazia.
Métodos hexadecimais são frequentemente usados como ataques
Observe que esse método hexadecimal é frequentemente usado como um ataque de injeção SQL, onde números inteiros são exatamente como strings e escapam apenas com ele
mysql_real_escape_string
. Então você pode evitar o uso de aspas.Por exemplo, se você apenas fizer algo assim:
um ataque pode injetá-lo com muita facilidade . Considere o seguinte código injetado retornado do seu script:
e agora apenas extraia a estrutura da tabela:
E, em seguida, basta selecionar os dados desejados. Não é legal?
Porém, se o codificador de um site injetável o encurtasse, nenhuma injeção seria possível porque a consulta teria a seguinte aparência:
SELECT ... WHERE id = UNHEX('2d312075...3635')
fonte
+
mas comCONCAT
. E para o desempenho: Eu não acho que afeta o desempenho porque o MySQL tem que analisar dados e não importa se a origem é string ou hex'root'
ou você pode enfeitiçar,0x726f6f74
mas se quiser um número e enviá-lo como string, provavelmente escreverá '42' e não CHAR (42). ) ... '42' em hexadecimal seria0x3432
não0x42
SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ')
Prevenção de injeção - mysql_real_escape_string ()
O PHP possui uma função especialmente criada para evitar esses ataques. Tudo que você precisa fazer é usar a boca cheia de uma função
mysql_real_escape_string
,.mysql_real_escape_string
pega uma string que será usada em uma consulta MySQL e retorna a mesma string com todas as tentativas de injeção de SQL escapadas com segurança. Basicamente, ele substituirá as aspas problemáticas (') que um usuário pode digitar por um substituto seguro para MySQL, uma aspas escapadas \'.NOTA: você deve estar conectado ao banco de dados para usar esta função!
// Conecte-se ao MySQL
Você pode encontrar mais detalhes no MySQL - SQL Injection Prevention .
fonte
mysql_real_escape_string
objetivo está em permitir construir consulta SQL correta para cada string de dados de entrada. A prevenção de injeção de sql é o efeito colateral dessa função.mysql_real_escape_string()
não é infalível .mysql_real_escape_string
agora está obsoleto, portanto, não é mais uma opção viável. Ele será removido no futuro do PHP. É melhor seguir o que o pessoal do PHP ou MySQL recomenda.Você poderia fazer algo básico assim:
Isso não resolverá todos os problemas, mas é um trampolim muito bom. Eu deixei de lado itens óbvios, como verificar a existência, o formato da variável (números, letras etc.).
fonte
$q = "SELECT col FROM tbl WHERE x = $safe_var";
por exemplo. A configuração$safe_var
para1 UNION SELECT password FROM users
funciona neste caso devido à falta de aspas. Também é possível injetar strings na consulta usandoCONCAT
eCHR
.mysql_real_escape_string()
não é infalível .mysql_real_escape_string
agora está obsoleto, portanto, não é mais uma opção viável. Ele será removido no futuro do PHP. É melhor seguir o que o pessoal do PHP ou MySQL recomenda.Tudo o que você acabar usando, verifique se sua entrada já não foi destruída por
magic_quotes
algum lixo bem intencionado e, se necessário, execute-astripslashes
ou o que for para higienizá-la.fonte
Consulta parametrizada E validação de entrada é o caminho a percorrer. Existem muitos cenários em que a injeção de SQL pode ocorrer, mesmo que
mysql_real_escape_string()
tenha sido usada.Esses exemplos são vulneráveis à injeção de SQL:
ou
Nos dois casos, você não pode usar
'
para proteger o encapsulamento.Fonte : A injeção inesperada de SQL (quando o escape não é suficiente)
fonte
Na minha opinião, a melhor maneira de impedir geralmente a injeção de SQL no seu aplicativo PHP (ou em qualquer aplicativo da Web) é pensar na arquitetura do seu aplicativo. Se a única maneira de se proteger contra a injeção de SQL é se lembrar de usar um método ou função especial que faça a coisa certa toda vez que você conversar com o banco de dados, estará fazendo errado. Dessa forma, é apenas uma questão de tempo até que você esqueça de formatar corretamente sua consulta em algum momento do seu código.
Adotar o padrão MVC e uma estrutura como CakePHP ou CodeIgniter é provavelmente o caminho a seguir: Tarefas comuns, como a criação de consultas seguras ao banco de dados, foram resolvidas e implementadas centralmente nessas estruturas. Eles ajudam a organizar seu aplicativo da Web de maneira sensata e fazem você pensar mais em carregar e salvar objetos do que em construir com segurança consultas SQL únicas.
fonte
Eu sou a favor de procedimentos armazenados (o MySQL tem suporte a procedimentos armazenados desde o 5.0 ) do ponto de vista da segurança - as vantagens são -
As desvantagens são -
fonte
Existem muitas maneiras de impedir injeções de SQL e outros hacks de SQL. Você pode encontrá-lo facilmente na Internet (Pesquisa do Google). Claro que a DOP é uma das boas soluções. Mas eu gostaria de sugerir uma boa prevenção de links da injeção de SQL.
O que é injeção de SQL e como prevenir
Manual PHP para injeção de SQL
Explicação da Microsoft sobre injeção e prevenção de SQL em PHP
E outros como Prevenir a injeção de SQL com MySQL e PHP .
Agora, por que você precisa impedir que sua consulta seja injetada em SQL?
Gostaria de informar: Por que tentamos impedir a injeção de SQL com um pequeno exemplo abaixo:
Consulta para correspondência de autenticação de login:
Agora, se alguém (um hacker) colocar
e senha qualquer coisa ....
A consulta será analisada no sistema apenas até:
A outra parte será descartada. Então o que vai acontecer? Um usuário não autorizado (hacker) poderá fazer login como administrador sem ter sua senha. Agora, ele / ela pode fazer qualquer coisa que o administrador / pessoa de email possa fazer. Veja, é muito perigoso se a injeção de SQL não for impedida.
fonte
Eu acho que se alguém quiser usar PHP e MySQL ou algum outro servidor de banco de dados:
(int)$foo
. Leia mais sobre o tipo de variáveis em PHP aqui . Se você estiver usando bibliotecas como PDO ou MySQLi, sempre use PDO :: quote () e mysqli_real_escape_string () .Exemplos de bibliotecas:
---- DOP
--- MySQLi
PS :
A DOP vence esta batalha com facilidade. Com suporte para doze drivers de banco de dados diferentes e parâmetros nomeados, podemos ignorar a pequena perda de desempenho e nos acostumar com sua API. Do ponto de vista da segurança, os dois estão seguros desde que o desenvolvedor os use da maneira que deveriam ser usados
Mas, embora o PDO e o MySQLi sejam bastante rápidos, o MySQLi executa insignificantemente mais rápido em benchmarks - ~ 2,5% para declarações não preparadas e ~ 6,5% para declarações preparadas.
E teste todas as consultas ao seu banco de dados - é a melhor maneira de evitar a injeção.
fonte
Se possível, lance os tipos de seus parâmetros. Mas só funciona em tipos simples como int, bool e float.
fonte
Se você quiser aproveitar os mecanismos de cache, como Redis ou Memcached , talvez o DALMP possa ser uma opção. Ele usa MySQLi puro . Verifique isto: Camada de abstração de banco de dados DALMP para MySQL usando PHP.
Além disso, você pode 'preparar' seus argumentos antes de preparar sua consulta, para poder criar consultas dinâmicas e, no final, ter uma consulta de instruções totalmente preparada. Camada de Abstração de Banco de Dados DALMP para MySQL usando PHP.
fonte
Para quem não tem certeza de como usar o DOP (proveniente das
mysql_
funções), criei um invólucro do PDO muito, muito simples, que é um único arquivo. Existe para mostrar como é fácil fazer todas as coisas comuns que os aplicativos precisam ser executados. Funciona com PostgreSQL, MySQL e SQLite.Basicamente, lê-lo enquanto você ler o manual para ver como colocar as funções de DOP para uso na vida real para torná-lo simples para armazenar e recuperar valores no formato que você quiser.
fonte
Usando esta função PHP,
mysql_escape_string()
você pode obter uma boa prevenção de maneira rápida.Por exemplo:
mysql_escape_string
- Escapa uma string para uso em um mysql_queryPara mais prevenção, você pode adicionar no final ...
Finalmente você obtém:
fonte
Algumas diretrizes para escapar caracteres especiais nas instruções SQL.
Não use MySQL . Esta extensão está obsoleta. Use MySQLi ou PDO .
MySQLi
Para escapar manualmente caracteres especiais em uma string, você pode usar a função mysqli_real_escape_string . A função não funcionará corretamente, a menos que o conjunto de caracteres correto seja definido com mysqli_set_charset .
Exemplo:
Para escape automático de valores com instruções preparadas, use mysqli_prepare e mysqli_stmt_bind_param em que tipos para as variáveis de ligação correspondentes devem ser fornecidos para uma conversão apropriada:
Exemplo:
Não importa se você usa instruções preparadas ou
mysqli_real_escape_string
, sempre precisa saber o tipo de dados de entrada com os quais está trabalhando.Portanto, se você usar uma instrução preparada, deverá especificar os tipos das variáveis para a
mysqli_stmt_bind_param
função.E o uso de
mysqli_real_escape_string
é para, como o nome diz, escapar caracteres especiais em uma string, para que não torne inteiros seguros. O objetivo desta função é impedir a quebra de cadeias de caracteres nas instruções SQL e os danos ao banco de dados que isso pode causar.mysqli_real_escape_string
é uma função útil quando usada corretamente, especialmente quando combinada comsprintf
.Exemplo:
fonte
A alternativa simples para esse problema pode ser resolvida concedendo permissões apropriadas no próprio banco de dados. Por exemplo: se você estiver usando um banco de dados MySQL, entre no banco de dados através do terminal ou da interface do usuário fornecida e siga este comando:
Isso restringirá o usuário a ficar confinado apenas com as consultas especificadas. Remova a permissão de exclusão e os dados nunca serão excluídos da consulta acionada na página PHP. A segunda coisa a fazer é liberar os privilégios para que o MySQL atualize as permissões e atualizações.
mais informações sobre descarga .
Para ver os privilégios atuais do usuário, ative a seguinte consulta.
Saiba mais sobre o GRANT .
fonte
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.
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:
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 :
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 :
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:
Log de consulta:
MySQLi:
Log de consulta:
É claro que uma declaração preparada também está escapando dos dados, nada mais.
Como também mencionado na declaração acima,
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:
fonte
Eu uso três maneiras diferentes para impedir que meu aplicativo Web fique vulnerável à injeção de SQL.
mysql_real_escape_string()
, que é uma função pré-definida em PHP , e este código add barras invertidas para os seguintes caracteres:\x00
,\n
,\r
,\
,'
,"
e\x1a
. Passe os valores de entrada como parâmetros para minimizar a chance de injeção de SQL.Eu espero que isso te ajude.
Considere a seguinte consulta:
$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
mysql_real_escape_string () não protegerá aqui. Se você usar aspas simples ('') em torno de suas variáveis na sua consulta, é o que o protege contra isso. Aqui está uma solução abaixo para isso:
$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
Esta pergunta tem algumas boas respostas sobre isso.
Eu sugiro, usar o DOP é a melhor opção.
Editar:
mysql_real_escape_string()
está obsoleto a partir do PHP 5.5.0. Use mysqli ou PDO.Uma alternativa para mysql_real_escape_string () é
Exemplo:
fonte
Uma maneira simples seria usar uma estrutura PHP como CodeIgniter ou Laravel, que possui recursos embutidos como filtragem e registro ativo, para que você não precise se preocupar com essas nuances.
fonte
Aviso: a abordagem descrita nesta resposta se aplica apenas a cenários muito específicos e não é segura, pois os ataques de injeção SQL não dependem apenas da capacidade de injetar
X=Y
.Se os invasores estiverem tentando invadir o formulário através da
$_GET
variável do PHP ou com a string de consulta da URL, você poderá capturá-los se eles não estiverem seguros.Porque
1=1
,2=2
,1=2
,2=1
,1+1=2
, etc ... são as perguntas comuns a um banco de dados SQL de um atacante. Talvez também seja usado por muitos aplicativos de hackers.Mas você deve ter cuidado para não reescrever uma consulta segura em seu site. O código acima fornece uma dica para reescrever ou redirecionar (depende de você) a sequência de consultas dinâmicas específicas de hackers em uma página que armazena o endereço IP do invasor ou até mesmo seus cookies, histórico, navegador ou qualquer outra informação sensível informações, para que você possa lidar com eles posteriormente, banindo a conta ou entrando em contato com as autoridades.
fonte
1-1=0
? :)([0-9\-]+)=([0-9]+)
.Existem muitas respostas para PHP e MySQL , mas aqui está o código para PHP e Oracle para impedir a injeção de SQL e o uso regular de drivers oci8:
fonte
Uma boa idéia é usar um mapeador objeto-relacional como o Idiorm :
Ele não apenas economiza injeções de SQL, mas também erros de sintaxe! Ele também suporta coleções de modelos com encadeamento de métodos para filtrar ou aplicar ações a vários resultados ao mesmo tempo e várias conexões.
fonte
Usar o PDO e o MYSQLi é uma boa prática para evitar injeções de SQL, mas se você realmente deseja trabalhar com funções e consultas do MySQL, seria melhor usar
mysql_real_escape_string
Existem mais habilidades para evitar isso: como identificar - se a entrada é uma string, número, caractere ou matriz, existem muitas funções embutidas para detectar isso. Além disso, seria melhor usar essas funções para verificar os dados de entrada.
is_string
is_numeric
E é muito melhor usar essas funções para verificar os dados de entrada
mysql_real_escape_string
.fonte
mysql_real_escape_string()
não é infalível .mysql_real_escape_string
agora está obsoleto, portanto, não é mais uma opção viável. Ele será removido do PHP no futuro. É melhor seguir o que o pessoal do PHP ou MySQL recomenda.Eu escrevi essa pequena função há vários anos:
Isso permite a execução de instruções em uma cadeia de caracteres C # de uma linha.
Escapa considerando o tipo de variável. Se você tentar parametrizar nomes de tabelas e colunas, isso falhará, pois coloca todas as seqüências entre aspas, que é uma sintaxe inválida.
ATUALIZAÇÃO DE SEGURANÇA: A
str_replace
versão anterior permitia injeções adicionando {#} tokens nos dados do usuário. Estapreg_replace_callback
versão não causa problemas se a substituição contiver esses tokens.fonte