Eu fiz o seguinte procedimento armazenado:
ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender
Agora, tentei fazer algo assim. Talvez eu esteja fazendo isso errado, mas quero ter certeza de que esse procedimento pode impedir qualquer injeção de SQL:
EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'
A imagem abaixo mostra o SQL acima sendo executado no SSMS e os resultados sendo exibidos corretamente em vez de um erro:
Btw, eu adicionei essa parte após o ponto-e-vírgula após a execução da consulta. Então eu o executei novamente, mas quando verifiquei se a tabela tblActor existe ou não, ela ainda estava lá. Estou fazendo algo errado? Ou isso é realmente à prova de injeção? Eu acho que o que estou tentando perguntar aqui também é que é um procedimento armazenado como este seguro? Obrigado.
EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
Respostas:
Este código funciona corretamente porque é:
Para que o SQL Injection funcione, é necessário criar uma string de consulta (que não está sendo executada) e não converter apóstrofos únicos (
'
) em apóstrofes-escapadas (''
) (que são escapadas pelos parâmetros de entrada).Na sua tentativa de passar um valor "comprometido", a
'Male; DROP TABLE tblActor'
string é apenas isso, uma string simples.Agora, se você estivesse fazendo algo como:
em seguida, que seria suscetível a injeção SQL porque que consulta não está no atual, o contexto pré-analisado; essa consulta é apenas outra string no momento. Portanto, o valor de
@InputParam
poderia ser'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
e isso pode representar um problema, porque essa consulta seria renderizada e executada como:Esse é um (dos vários) principais motivos para usar Procedimentos Armazenados: inerentemente mais seguro (bem, desde que você não contorne essa segurança, criando consultas como eu mostrei acima, sem validar os valores de quaisquer parâmetros usados). Embora se você precisar criar o Dynamic SQL, a maneira preferida é parametrizar isso também usando
sp_executesql
:Usando essa abordagem, alguém que tentasse passar
'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
umDATETIME
parâmetro de entrada obteria um erro ao executar o Procedimento Armazenado. Ou mesmo se o Procedimento armazenado aceitasse@InputParameter
comoNVARCHAR(100)
, teria que converter para aDATETIME
para passar para asp_executesql
chamada. E mesmo se o parâmetro no SQL dinâmico for um tipo de string, entrando no Procedimento armazenado em primeiro lugar, qualquer apóstrofo único seria automaticamente escapado para um apóstrofo duplo.Há um tipo de ataque menos conhecido no qual o invasor tenta preencher o campo de entrada com apóstrofes, de modo que uma sequência dentro do Stored Procedure que seria usada para construir o Dynamic SQL, mas que é declarada pequena demais, não se encaixa em tudo e empurra para fora o apóstrofo final e, de alguma forma, acaba com o número correto de apóstrofos, para não ser mais "escapado" dentro da string. Isso se chama Truncamento SQL e foi comentado em um artigo da revista MSDN intitulado "Novos ataques de truncamento SQL e como evitá-los", de Bala Neerumalla, mas o artigo não está mais online. O problema que contém este artigo - a edição de novembro de 2006 da MSDN Magazine - está disponível apenas como um arquivo de Ajuda do Windows (em .chmformato). Se você fizer o download, ele poderá não abrir devido às configurações de segurança padrão. Se isso acontecer, clique com o botão direito do mouse no arquivo MSDNMagazineNovember2006en-us.chm e selecione "Propriedades". Em uma dessas guias, haverá uma opção para "Confiar neste tipo de arquivo" (ou algo parecido) que precisa ser verificado / ativado. Clique no botão "OK" e tente abrir o arquivo .chm novamente.
Outra variação do ataque de truncamento é, supondo que uma variável local seja usada para armazenar o valor "seguro" fornecido pelo usuário, pois as aspas simples dobraram para serem escapadas, para preencher essa variável local e colocar a aspas simples no fim. A idéia aqui é que, se a variável local não for dimensionada adequadamente, não haverá espaço suficiente no final para a segunda aspas simples, deixe a variável terminada com uma aspas simples que depois será combinada com a aspas simples que finaliza o valor literal no SQL dinâmico, transformando essa aspas simples em aspas simples de escape incorporadas, e a string literal no Dynamic SQL termina com a próxima aspas simples que se destinava a iniciar a próxima string literal. Por exemplo:
Aqui, o SQL dinâmico a ser executado é agora:
Esse mesmo SQL dinâmico, em um formato mais legível, é:
Consertar isso é fácil. Basta seguir um destes procedimentos:
Não use uma variável local para armazenar o valor "fixo"; basta colocar o
REPLACE()
diretamente na criação do Dynamic SQL:O SQL dinâmico não está mais comprometido:
Notas sobre o exemplo de Trunction acima:
DELETE tableName
seja destrutivo, mas é menos provável que você adicione um usuário externo ou altere uma senha de administrador.Para obter informações mais detalhadas relacionadas à injeção de SQL (cobrindo vários RDBMS e cenários), consulte o seguinte no Projeto de segurança de aplicativos da Web abertos (OWASP):
Testando a injeção de SQL
Resposta relacionada ao estouro de pilha relacionada à injeção e truncamento de SQL:
Qual é a segurança do T-SQL após a substituição do caractere de escape?
fonte
A questão simples é que você não está confundindo dados com comando. Os valores para os parâmetros nunca são tratados como parte do comando e, portanto, nunca são executados.
Eu escrevi sobre isso em: http://blogs.lobsterpot.com.au/2015/02/10/sql-injection-the-golden-rule/
fonte