Em termos de injeção de SQL , eu entendo perfeitamente a necessidade de parametrizar um string
parâmetro; esse é um dos truques mais antigos do livro. Mas quando pode ser justificado não parametrizar um SqlCommand
? Algum tipo de dado é considerado "seguro" para não parametrizar?
Por exemplo: Eu não me considero em qualquer lugar perto de um especialista em SQL, mas eu não consigo pensar em nenhum caso em que seria potencialmente vulnerável a injeção SQL para aceitar um bool
ou um int
e apenas concatenar-lo direto para a consulta.
Minha suposição está correta ou isso poderia potencialmente deixar uma grande vulnerabilidade de segurança em meu programa?
Para esclarecimento, esta questão está marcada c #que é uma linguagem fortemente tipada; quando digo "parâmetro", pense algo como public int Query(int id)
.
fonte
Respostas:
Acho que é seguro ... tecnicamente , mas é um péssimo hábito de adquirir. Você realmente deseja escrever consultas como esta?
Também o deixa vulnerável na situação em que um tipo muda de um inteiro para uma string (pense no número do funcionário que, apesar do nome - pode conter letras).
Portanto, alteramos o tipo de EmployeeNumber de
int
parastring
, mas esquecemos de atualizar nossas consultas sql. Opa.fonte
AddWithValue
? blogs.msmvps.com/jcoehoorn/blog/2014/05/12/…AddWithValue
menos que tenha um mapeamento de tipos de banco de dados como parte da construção dinâmica da instrução. Eu presumo que você tenha uma lista de colunas de destino e, como parte do dicionário, você pode ter os tipos, se desejar. Caso contrário, apenas pegue o impacto no desempenho. No final das contas, acho que é apenas uma boa informação saber.AddWithValue
já?" deve realmente ser "se você conhece o tipo, então deve evitar o usoAddWithValue
.Ao usar uma plataforma fortemente tipado em um computador que você controle (como um servidor web), você pode evitar a injeção de código para consultas com apenas
bool
,DateTime
ouint
valores (e outros numérico). O que é uma preocupação são os problemas de desempenho causados por forçar o sql server a recompilar todas as consultas e por impedi-lo de obter boas estatísticas sobre quais consultas são executadas com que frequência (o que prejudica o gerenciamento do cache).Mas a parte "em um computador você controla" é importante, porque caso contrário, um usuário pode alterar o comportamento usado pelo sistema para gerar strings a partir desses valores para incluir texto arbitrário.
Também gosto de pensar a longo prazo. O que acontece quando a base de código fortemente tipificada de hoje é portada por meio da tradução automática para a linguagem dinâmica new-hotness e você de repente perde a verificação de tipo, mas ainda não tem todos os testes de unidade corretos para o código dinâmico ?
Realmente, não há um bom motivo para não usar parâmetros de consulta para esses valores. É a maneira certa de fazer isso. Vá em frente e insira os valores de código na string sql quando eles realmente forem constantes, mas caso contrário, por que não usar apenas um parâmetro? Não é como se fosse difícil.
No final das contas, eu não chamaria isso de bug per se, mas sim de cheiro : algo que não chega a ser um bug por si só, mas é uma forte indicação de que os bugs estão próximos ou estarão eventualmente. Um bom código evita deixar odores, e qualquer boa ferramenta de análise estática sinalizará isso.
Acrescentarei que este não é, infelizmente, o tipo de argumento que você pode ganhar diretamente. Parece uma situação em que estar "certo" não é mais suficiente, e pisar na ponta dos pés de seus colegas de trabalho para corrigir esse problema por conta própria provavelmente não promoverá uma boa dinâmica de equipe; em última análise, pode doer mais do que ajuda. Uma abordagem melhor neste caso pode ser promover o uso de uma ferramenta de análise estática. Isso daria legitimidade e credibilidade aos esforços direcionados e voltando e corrigindo o código existente.
fonte
DateTime
ouint
Em alguns casos, É possível realizar um ataque de injeção de SQL com variáveis não parametrizadas (concatenadas) diferentes de valores de string - consulte este artigo de Jon: http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables -cultura / .
O fato é que, quando
ToString
é chamado, algum provedor de cultura customizado pode transformar um parâmetro não string em sua representação string que injeta algum SQL na consulta.fonte
int
s?”CultureInfo
difícil saber por que você precisaria da injeção de SQL de qualquer maneira.Isso não é seguro, mesmo para tipos que não sejam string. Sempre use parâmetros. Período.
Considere o seguinte exemplo de código:
À primeira vista, o código parece seguro, mas tudo muda se você fizer algumas alterações nas Configurações Regionais do Windows e adicionar injeção em formato de data abreviada:
Agora, o texto de comando resultante tem a seguinte aparência:
O mesmo pode ser feito para o
int
tipo, pois o usuário pode definir o sinal negativo personalizado, que pode ser facilmente alterado para injeção SQL.Alguém poderia argumentar que a cultura invariável deve ser usada em vez da cultura atual, mas eu tenho visto concatenações de string como esta tantas vezes e é muito fácil perder ao concatenar strings com objetos usando
+
.fonte
int
tipo?"SELECT * FROM Table1 WHERE Id =" + intVariable.ToString ()
Segurança
Está tudo bem.
Os invasores não podem injetar nada em sua variável int digitada.
Desempenho
não OK.
É melhor usar parâmetros, então a consulta será compilada uma vez e armazenada em cache para o próximo uso. Da próxima vez, mesmo com valores de parâmetro diferentes, a consulta é armazenada em cache e não precisa ser compilada no servidor de banco de dados.
Estilo de codificação
Má prática.
"SELECT * FROM Product WHERE Id =" + TextBox1.Text
Embora não seja sua pergunta, mas talvez útil para leitores futuros:
Desastre de segurança !
Mesmo quando o
Id
campo é inteiro, sua consulta pode estar sujeita à injeção de SQL. Suponha que você tenha uma consulta em seu aplicativo"SELECT * FROM Table1 WHERE Id=" + TextBox1.Text
. Um invasor pode inserir na caixa de texto1; DELETE Table1
e a consulta será:Se você não quiser usar a consulta parametrizada aqui, você deve usar os valores digitados:
Sua pergunta
Acho que mudar esses códigos não é perda de tempo. Na verdade, a mudança é recomendada!
se seu colega de trabalho usa variáveis int, não há risco de segurança, mas acho que alterar esses códigos não é perda de tempo e, de fato, é recomendável alterar esses códigos. Torna o código mais legível, mais sustentável e torna a execução mais rápida.
fonte
.ToString()
é determinado por um item de configuração do sistema operacional que é fácil de alterar para incluir texto arbitrário.Na verdade, existem duas perguntas em uma. E a questão do título tem muito pouco a ver com as preocupações expressas pelo OP nos comentários posteriores.
Embora eu perceba que para o OP é o caso particular que importa, para os leitores vindos do Google, é importante responder à pergunta mais geral, que pode ser formulada como "a concatenação é tão segura quanto as declarações preparadas se eu tivesse certeza que cada literal que estou concatenando é seguro? ". Então, eu gostaria de me concentrar neste último. E a resposta é
Definitivamente não.
A explicação não é tão direta como a maioria dos leitores gostaria, mas vou tentar o meu melhor.
Estive refletindo sobre o assunto por um tempo, resultando no artigo (embora baseado no ambiente PHP) onde tentei resumir tudo. Ocorreu-me que a questão da proteção contra injeção de SQL costuma ser evitada em relação a alguns tópicos relacionados, mas mais restritos, como escape de string, conversão de tipo e outros. Embora algumas das medidas possam ser consideradas seguras quando tomadas por si mesmas, não existe um sistema, nem uma regra simples a seguir. O que torna o terreno muito escorregadio, colocando muito na atenção e experiência do desenvolvedor.
A questão da injeção de SQL não pode ser simplificada para uma questão de sintaxe específica. É mais amplo do que o desenvolvedor médio costumava pensar. É uma questão metodológica também. Não se trata apenas de "Qual formatação específica devemos aplicar", mas também de " Como isso deve ser feito".
(Deste ponto de vista, um artigo de Jon Skeet citado na outra resposta está indo bem mais mal do que bem, já que está novamente criticando algum caso extremo, concentrando-se em um problema de sintaxe específico e falhando em resolver o problema como um todo.)
Quando você está tentando abordar a questão da proteção não como um todo, mas como um conjunto de diferentes questões de sintaxe, você está enfrentando uma infinidade de problemas.
Ao contrário dessa bagunça, as declarações preparadas são de fato O Santo Graal:
(Pensando mais, descobri que o conjunto atual de marcadores de posição não é suficiente para as necessidades da vida real e deve ser estendido, tanto para as estruturas de dados complexas, como matrizes, e até mesmo palavras-chave SQL ou identificadores, que às vezes precisam ser adicionados ao consulta dinamicamente também, mas um desenvolvedor é deixado desarmado para tal caso e forçado a voltar para a concatenação de string, mas isso é uma outra questão).
Curiosamente, a controvérsia desta questão é provocada pela natureza muito controversa de Stack Overflow. A ideia do site é fazer uso de perguntas específicas de usuários que perguntam diretamente para atingir o objetivo de ter um banco de dados de respostas de propósito geral adequado para usuários que vêm de pesquisa . A ideia não é ruim em si , mas falha em uma situação como esta: quando um usuário faz uma pergunta muito restrita , principalmente para obter uma discussão em uma disputa com um colega (ou para decidir se vale a pena refatorar o código). Enquanto a maioria dos participantes experientes tenta escrever uma resposta, tendo em mente a missão de Stack Overflow como um todo, tornando sua resposta adequada para o maior número possível de leitores, não apenas para o OP.
fonte
Não vamos pensar apenas em considerações de segurança ou proteção de tipos.
O motivo pelo qual você usa consultas parametrizadas é para melhorar o desempenho no nível do banco de dados. De uma perspectiva de banco de dados, uma consulta parametrizada é uma consulta no buffer SQL (para usar a terminologia do Oracle, embora eu imagine que todos os bancos de dados tenham um conceito semelhante internamente). Assim, o banco de dados pode conter uma certa quantidade de consultas na memória, preparadas e prontas para execução. Essas consultas não precisam ser analisadas e serão mais rápidas. As consultas executadas com frequência geralmente estarão no buffer e não precisarão ser analisadas toda vez que forem usadas.
A MENOS QUE
Alguém não usa consultas parametrizadas. Neste caso, o buffer é continuamente liberado por um fluxo de consultas quase idênticas, cada uma das quais precisa ser analisada e executada pelo mecanismo de banco de dados e o desempenho é prejudicado, pois mesmo as consultas frequentemente executadas acabam sendo analisadas novamente muitas vezes por dia. Tenho sintonizado bancos de dados para viver e esta tem sido uma das maiores fontes de frutas ao alcance.
AGORA
Para responder à sua pergunta, SE sua consulta tiver um pequeno número de valores numéricos distintos, você provavelmente não estará causando problemas e pode, de fato, melhorar o desempenho infinitesimalmente. SE, entretanto, houver potencialmente centenas de valores e a consulta for muito chamada, você afetará o desempenho do seu sistema, portanto, não o faça.
Sim, você pode aumentar o buffer SQL, mas em última análise, é sempre às custas de outros usos mais críticos para a memória, como índices de cache ou dados. Moral, use consultas parametrizadas religiosamente para que você possa otimizar seu banco de dados e usar mais memória do servidor para as coisas que importam ...
fonte
Para adicionar algumas informações à resposta de Maciek:
É fácil alterar as informações de cultura de um aplicativo de terceiros .NET chamando a função principal do assembly por reflexão:
Isso só funciona se a função principal do BobbysApp for pública. Se Main não for público, pode haver outras funções públicas que você pode chamar.
fonte
Na minha opinião, se você pode garantir que o parâmetro com o qual está trabalhando nunca conterá uma string, é seguro, mas eu não faria isso em nenhum caso. Além disso, você verá uma ligeira queda no desempenho devido ao fato de estar executando a concatenação. A pergunta que eu faria é por que você não quer usar parâmetros?
fonte
Está tudo bem, mas nunca é seguro .. e a segurança sempre depende das entradas, por exemplo se o objeto de entrada for TextBox, os atacantes podem fazer algo complicado já que a caixa de texto pode aceitar string, então você tem que colocar algum tipo de validação / conversão para ser capaz de evitar a entrada errada dos usuários. Mas o fato é que não é seguro. Simples assim.
fonte
Não, você pode obter um ataque de injeção de SQL dessa forma. Escrevi um antigo artigo em turco que mostra como fazer aqui . Exemplo de artigo em PHP e MySQL, mas o conceito funciona da mesma forma em C # e SQL Server.
Basicamente, você ataca o seguinte caminho. Vamos considerar que você tem uma página que mostra informações de acordo com o valor do id inteiro. Você não parametrizou isso em valor, como abaixo.
Ok, suponho que você esteja usando o MySQL e eu ataco da seguinte maneira.
Observe que aqui o valor injetado não é string. Estamos alterando o valor char para int usando a função ASCII. Você pode realizar a mesma coisa no SQL Server usando "CAST (YourVarcharCol AS INT)".
Depois disso, uso as funções de comprimento e substring para descobrir o nome do seu banco de dados.
Então, usando o nome do banco de dados, você começa a obter nomes de tabelas no banco de dados.
Claro que você tem que automatizar este processo, uma vez que você obtém apenas UM caractere por consulta. Mas você pode automatizar isso facilmente. Meu artigo mostra um exemplo em watir . Usando apenas uma página e valor de ID não parametrizado. Posso aprender cada nome de tabela em seu banco de dados. Depois disso, posso procurar tabelas importantes. Isso vai levar algum tempo, mas é possível.
fonte
public void QuerySomething(int id) { // this will only accept an integer }
Bem ... uma coisa é certa: Segurança NÃO está ok, quando você concatenar uma string (tomada pelo usuário) com sua string de comando SQL. Não importa sempre que a cláusula where se refere a um inteiro ou a qualquer tipo; podem ocorrer injeções.
O que importa no SQL Injection é o tipo de dados da variável que costumava obter o valor do usuário.
Supondo que tenhamos um número inteiro na cláusula where e:
a variável do usuário é uma string. Então ok, não é muito fácil de injetar (usando UNION), mas é muito fácil de contornar usando ataques do tipo 'OR 1 = 1' ...
Se a variável do usuário for um inteiro. Então, novamente podemos 'testar' a força do sistema, passando no teste de grandes números incomuns para travamentos do sistema ou mesmo para um estouro de buffer oculto (na string final) ...;)
Talvez os parâmetros para consultas ou (melhor ainda - imo) para Procedimentos armazenados não sejam 100% seguros contra ameaças, mas são a medida menos necessária (ou elementar se você preferir) para minimizá-los.
fonte