Pergunta

Se eu selecionar uma linha de uma tabela que tem basicamente duas opções, ou como este

int key = some_number_derived_from_a_dropdown_or_whatever
SqlCommand cmd = new SqlCommand("select * from table where primary_key = " + key.ToString());

ou usar um parâmetro

SqlCommand cmd = new SqlCommand("select * from table where primary_key = @pk");
SqlParameter param  = new SqlParameter();
param.ParameterName = "@pk";
param.Value         = some_number_derived_from_a_dropdown_or_whatever;
cmd.Parameters.Add(param);

Agora, eu sei que o primeiro método é desaprovado por causa de possíveis ataques de injeção SQL, mas neste caso o parâmetro é um inteiro e, portanto, não deveria ser possível injetar código malicioso.

A minha pergunta é esta: Do que você use a opção 1 no código de produção, porque você considerar o seguro uso por causa da facilidade de uso e controle sobre o parâmetro inserido (como o acima, ou se o parâmetro é criado no código)? Ou você sempre usar parâmetros não importa o quê? São parâmetros 100% seguro injeção?

Foi útil?

Solução

eu vou pular o argumento de injeção SQL, que é muito bem conhecida e se concentrar apenas no aspecto SQL de parâmetros versus não parâmetros.

Quando você enviar um lote SQL para o servidor, qualquer lote, tem que ser analisado para ser compreendido. Como qualquer outro compilador, o compilador SQL tem de produzir um AST do texto e, em seguida, operar em a árvore de sintaxe. Em última análise, o otimizador transforma a árvore de sintaxe em uma árvore de execução e, finalmente, produz um plano de execução e que é realmente executado. Voltar na idade das trevas de circa 1995 ele fez a diferença se o lote foi uma consulta Ad-Hoc ou um procedimento armazenado, mas hoje não faz absolutamente nenhum, todos eles o mesmo.

Agora, onde parâmetros fazer a diferença é que um cliente que envia uma consulta como select * from table where primary_key = @pk irá enviar exatamente o mesmo texto SQL todas as vezes, não importa o valor é interessado. O que acontece então é que o inteira processo que eu descrevi acima é curto-circuito. SQL irá procurar na memória um plano de execução para a matéria, não analisada, texto que recebeu (com base em um hash digerir da entrada) e, se encontrado, irá executar esse plano. Isso significa que nenhum de análise, sem otimização, nada, o lote vai em linha reta em execução . Em sistemas OLTP que rodam centenas e milhares de pequenos pedidos a cada segundo, esse caminho rápido faz uma diferença enorme desempenho.

Se você enviar a consulta no select * from table where primary_key = 1 forma, em seguida, SQL terá que pelo menos analisá-lo para entender o que está dentro do texto, já que o texto é provável um novo, diferente de qualquer lote anterior lo visto (mesmo um único caractere como 1 vs 2 faz com que todo o lote diferente). Ela irá então operar na árvore de sintaxe resultou e tentar um processo chamado Parametrização simples . Se a consulta pode ser paremeterized-auto, em seguida, SQL provavelmente vai encontrar um plano de execução em cache para ele de outras consultas que são executadas anteriormente com outros valores pk e reutilizar esse plano, por isso, pelo menos sua consulta não precisa ser otimizado e você pular o o passo de gerar um plano de execução real. Mas não média você conseguiu a curto-circuito completo, o caminho mais curto possível você conseguir com uma verdadeira consulta parametrizada cliente.

Você pode olhar para o SQL Server, SQL Estatísticas Objeto contador de desempenho do seu servidor. O contador Auto-Param Attempts/sec mostrará muitas vezes por segundo SQL tem que traduzir uma consulta recebida sem parâmetros em um único parametrizado automaticamente. Cada tentativa pode ser evitado se você parametrizar corretamente a consulta no cliente. Se você também tem um elevado número de Failed Auto-Params/sec é ainda pior, isso significa que as consultas estão indo o ciclo completo de otimização e plano de execução geração.

Outras dicas

Use sempre a opção 2).

O primeiro é não segura sobre ataques de injeção SQL .

O segundo é não só muito mais seguro, mas também terá um desempenho melhor porque o otimizador de consulta tem melhores chances de criar um bom plano de consulta para ele, porque os olhares de consulta o mesmo todo o tempo, esperamos que os parâmetros do curso.

Por que você deve evitar Opção 1

Mesmo que pareça que você está recebendo este valor de elemento select, alguém pode fingir um pedido HTTP e pós o que quiserem dentro de determinados campos. Seu código na opção 1 deve, pelo menos, substituir alguns caracteres / combinações perigosas (ie. Aspas simples, colchetes etc.).

Por que você está encorajado a usar a opção 2

mecanismo de consulta SQL é capaz de armazenar em cache plano de execução e gestão de estatísticas para várias consultas lançadas contra ele. consultas para ter unificadas (o melhor seria, evidentemente, ter um procedimento armazenado em separado) acelera a execução no longo prazo.

Eu ainda tenho que ouvir de qualquer exemplo onde os parâmetros podem ser sequestrado por injeção de SQL. Até eu ver que se prove o contrário, eu consideraria-los seguros.

SQL dinâmico nunca deve ser considerado seguro. Mesmo com "confiança" de entrada, é um mau hábito de entrar. Note que isso inclui SQL dinâmica em procedimentos armazenados; injeção de SQL pode ocorrer lá também.

Porque você tem o controle que o valor é um número inteiro, eles são praticamente equivalentes. I geralmente não utilizar a primeira forma, e normalmente, não permitem que o segundo tanto, porque geralmente não permitem acesso à tabela e requerem o uso de procedimentos armazenados. E, embora você pode fazer a execução SP sem usar a coleção de parâmetros, eu ainda recomendo usar SPs e parâmetros:

-- Not vulnerable to injection as long as you trust int and int.ToString()
int key = some_number_derived_from_a_dropdown_or_whatever ;
SqlCommand cmd = new SqlCommand("EXEC sp_to_retrieve_row " + key.ToString()); 

-- Vulnerable to injection all of a sudden
string key = some_number_derived_from_a_dropdown_or_whatever ;
SqlCommand cmd = new SqlCommand("EXEC sp_to_retrieve_row " + key.ToString()); 

Note que, embora a opção 1 é seguro em seu caso , o que acontece quando alguém vê e usa essa técnica com uma variável não-inteiro - agora você está treinando as pessoas a usar uma técnica que poderia ser aberto a injeção.

Note que você pode proativamente endurecer o seu banco de dados para injeção evitar ser eficaz mesmo quando o código do aplicativo está com defeito. Para contas / papéis que os aplicativos usam:

  • Apenas permitir o acesso a tabelas como absolutamente necessário
  • Só permitir o acesso a pontos de vista como absolutamente necessário
  • Não permita instruções DDL
  • Permitir executar para SPs em base papel
  • Qualquer SQL dinâmico em SPs devem ser revistos como absolutamente necessário

Pessoalmente, eu sempre utilizar a opção 2 como uma questão de hábito. Dito isto:

Uma vez que você está forçando a conversão no menu suspenso valor para um int, que irá fornecer alguma proteção contra ataques de injeção SQL. Ele alguém tenta injetar informações adicionais para as informações publicadas, C # irá lançar uma exceção ao tentar converter o código malicioso em um valor inteiro frustrando a tentativa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top