A injeção de SQL é um problema de segurança muito sério, em grande parte porque é muito fácil cometer erros: a maneira óbvia e intuitiva de criar uma consulta incorporando a entrada do usuário deixa você vulnerável, e a maneira certa de atenuá-lo exige que você conheça os parâmetros consultas e injeção de SQL primeiro.
Parece-me que a maneira óbvia de corrigir isso seria encerrar a opção óbvia (mas errada): conserte o mecanismo do banco de dados para que qualquer consulta recebida que use valores codificados na sua cláusula WHERE em vez de parâmetros retorne uma descrição agradável e descritiva mensagem de erro instruindo você a usar parâmetros. Obviamente, seria necessário ter uma opção de exclusão para que coisas como consultas ad-hoc de ferramentas administrativas ainda sejam executadas com facilidade, mas devem ser ativadas por padrão.
Isso interromperia a injeção de SQL a frio, quase da noite para o dia, mas até onde eu sei, nenhum RDBMS realmente faz isso. Existe alguma boa razão para não?
bad_ideas_sql = 'SELECT title FROM idea WHERE idea.status == "bad" AND idea.user == :mwheeler'
teria valores codificados e parametrizados em uma única consulta - tente entender isso! Eu acho que existem casos de uso válidos para essas consultas mistas.SELECT * FROM jokes WHERE date > DATE_SUB(NOW(), INTERVAL 1 DAY) ORDER BY score DESC;
"bad"
é realmente literal ou se resultou da concatenação de cadeias. As duas soluções que vejo são livrar-se do SQL e outras DSLs incorporadas a cadeias de caracteres (sim, por favor) ou promover linguagens em que a concatenação de cadeias de caracteres é mais irritante do que usar consultas parametrizadas (umm, não).Respostas:
Existem muitos casos em que o uso de um literal é a abordagem correta.
Do ponto de vista de desempenho, há momentos em que você deseja literais em suas consultas. Imagine que eu tenho um rastreador de bugs onde, quando ele fica grande o suficiente para se preocupar com o desempenho, espero que 70% dos bugs do sistema sejam "fechados", 20% serão "abertos", 5% estarão "ativos" e 5 % estará em algum outro status. Talvez eu queira razoavelmente ter a consulta que retorna todos os erros ativos
em vez de passar a
status
como uma variável de ligação. Eu quero um plano de consulta diferente, dependendo do valor transferidostatus
- eu gostaria de fazer uma varredura de tabela para retornar os bugs fechados e uma varredura de índice no diretóriostatus
coluna para retornar os empréstimos ativos. Agora, bancos de dados diferentes e versões diferentes têm abordagens diferentes para (com mais ou menos êxito) permitir que a mesma consulta use um plano de consulta diferente, dependendo do valor da variável de ligação. Mas isso tende a introduzir uma quantidade decente de complexidade que precisa ser gerenciada para equilibrar a decisão de se preocupar em analisar novamente uma consulta ou em reutilizar um plano existente para um novo valor de variável de ligação. Para um desenvolvedor, pode fazer sentido lidar com essa complexidade. Ou talvez faça sentido forçar um caminho diferente quando eu tiver mais informações sobre a aparência dos meus dados do que o otimizador.Do ponto de vista da complexidade do código, também há muitas vezes que faz todo sentido ter literais nas instruções SQL. Por exemplo, se você tem uma
zip_code
coluna com um CEP de 5 caracteres e às vezes possui 4 dígitos adicionais, faz todo o sentido fazer algo comoem vez de passar 4 parâmetros separados para os valores numéricos. Essas não são coisas que jamais mudarão, portanto, fazê-las vincular variáveis serve apenas para tornar o código potencialmente mais difícil de ler e criar o potencial de alguém vincular parâmetros na ordem errada e acabar com um bug.
fonte
A injeção de SQL ocorre quando uma consulta é criada concatenando o texto de uma fonte não confiável e não validada com outras partes de uma consulta. Embora tal coisa ocorra com mais frequência com literais de strings, essa não seria a única maneira de ocorrer. Uma consulta para valores numéricos pode levar uma string inserida pelo usuário (que é deveria conter apenas dígitos) e concatenar com outro material para formar uma consulta sem as aspas normalmente associadas aos literais da string; o código que confia demais na validação do lado do cliente pode ter coisas como nomes de campo provenientes de uma string de consulta HTML. Não há como o código olhar para uma string de consulta SQL ver como ela foi montada.
O importante não é se uma instrução SQL contém literais de cadeias, mas se uma sequência contém seqüências de caracteres de fontes não confiáveis e a validação para isso seria melhor manipulada na biblioteca que cria consultas. Geralmente, não há como o C # escrever código que permita uma literal de string, mas não permita outros tipos de expressão de string, mas pode-se ter uma regra de práticas de codificação que exige que as consultas sejam construídas usando uma classe de construção de consulta em vez de concatenação de string e qualquer pessoa que passe uma string não literal para o construtor de consultas deve justificar essa ação.
fonte
Se você quiser colocar os resultados no rodapé do seu fórum, será necessário adicionar um parâmetro fictício apenas para dizer falso sempre. Ou o ingênuo programador da Web pesquisa como desativar esse aviso e continua.
Agora você pode dizer que adicionaria uma exceção para enums, mas isso apenas abre o buraco novamente (embora menor). Sem mencionar que as pessoas primeiro precisam ser educadas para não usar
varchars
-las.O verdadeiro problema da injeção é construir programaticamente a string de consulta. A solução para isso é um mecanismo de procedimento armazenado e forçar seu uso ou uma lista de permissões de consultas permitidas.
fonte
deleted = false
porNOT deleted
, o que evita o literal. Mas o ponto é válido em geral.TL; DR : Você precisaria restringir todos os literais, não apenas os
WHERE
cláusulas. Por razões que não o fazem, ele permite que o banco de dados permaneça dissociado de outros sistemas.Em primeiro lugar, sua premissa é falha. Você deseja restringir apenas
WHERE
cláusulas, mas esse não é o único local para entrada do usuário. Por exemplo,Isso é igualmente vulnerável à injeção de SQL:
Portanto, você não pode restringir literais na
WHERE
cláusula. Você tem que restringir tudo literais.Agora ficamos com a pergunta: "Por que permitir literais?" Lembre-se disso: embora os bancos de dados relacionais sejam usados sob um aplicativo gravado em outro idioma em uma grande porcentagem do tempo, não é necessário que você use o código do aplicativo para usar o banco de dados. E aqui temos uma resposta: você precisa de literais para escrever código. A única outra alternativa seria exigir que todo o código fosse escrito em algum idioma independente do banco de dados. Portanto, tê-los oferece a capacidade de escrever "código" (SQL) diretamente no banco de dados. Este é um desacoplamento valioso e seria impossível sem literais. (Tente escrever em seu idioma favorito em algum momento sem literais. Tenho certeza de que você pode imaginar o quão difícil isso seria.)
Como um exemplo comum, os literais são frequentemente usados na população de tabelas de lista de valores / consulta:
Sem eles, você precisaria escrever código em outra linguagem de programação apenas para preencher esta tabela. A capacidade de fazer isso diretamente no SQL é valiosa .
Ficamos então com mais uma pergunta: por que as bibliotecas cliente de linguagem de programação não o fazem? E aqui temos uma resposta muito simples: eles teriam que reimplementar o analisador de banco de dados inteiro para cada versão suportada do banco de dados . Por quê? Porque não há outra maneira de garantir que você encontrou todos os literais. Expressões regulares não são suficientes. Por exemplo: este contém 4 literais separados no PostgreSQL:
Tentar fazer isso seria um pesadelo de manutenção, especialmente porque a sintaxe válida geralmente muda entre as principais versões dos bancos de dados.
fonte