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, eu tentei fazer algo assim. Talvez eu esteja fazendo isso errado, mas quero ter certeza de que tal procedimento pode impedir qualquer SQL Injection:
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 ao invés de um erro:
A propósito, adicionei essa parte após o ponto e vírgula depois que a consulta foi executada. Depois 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? Acho que o que estou tentando perguntar aqui também é que um procedimento armazenado como este é seguro? Obrigada.
Este código funciona corretamente porque é:
Para que o SQL Injection funcione, você deve criar uma string de consulta (o que você não está fazendo) e não converter apóstrofos únicos (
'
) em apóstrofos com escape (''
) (esses são escapados por meio dos parâmetros de entrada).Em 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:
então isso seria suscetível a SQL Injection porque essa consulta não está no contexto atual pré-analisado; essa consulta é apenas outra string no momento. Portanto, o valor de
@InputParam
poderia ser'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
e isso poderia apresentar um problema porque essa consulta seria renderizada e executada como:Este é um (de vários) motivos principais para usar Stored Procedures: inerentemente mais seguro (bem, contanto que você não contorne essa segurança criando consultas como mostrei acima sem validar os valores de quaisquer parâmetros usados). Porém, se você precisar criar um SQL dinâmico, a maneira preferida é parametrizar isso também usando
sp_executesql
:Usando essa abordagem, alguém tentando passar
'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
para umDATETIME
parâmetro de entrada obteria um erro ao executar o procedimento armazenado. Ou mesmo se o Stored Procedure aceitasse@InputParameter
comoNVARCHAR(100)
, ele teria que converter para aDATETIME
para poder passar para aquelasp_executesql
chamada. E mesmo que o parâmetro no SQL dinâmico seja um tipo de cadeia de caracteres, entrando no procedimento armazenado em primeiro lugar, qualquer apóstrofo único seria automaticamente escapado para um apóstrofo duplo.Existe um tipo de ataque menos conhecido no qual o invasor tenta preencher o campo de entrada com apóstrofos de forma que uma string dentro do procedimento armazenado que seria usada para construir o SQL dinâmico, mas declarada muito pequena, não cabe em tudo e empurra o apóstrofo final e de alguma forma termina com o número correto de apóstrofos para não ser mais "escapado" dentro da string. Isso é chamado SQL Truncation e foi falado 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. A edição 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 formato .chmformato). Se você baixá-lo, pode 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 marcada/ativada. 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 tinha aspas simples dobradas para serem escapadas, para preencher essa variável local e colocar as aspas simples no final. A ideia aqui é que, se a variável local não for dimensionada corretamente, não haverá espaço suficiente no final para a segunda aspa simples, deixe a variável terminando com uma aspa simples que combina com a aspa simples que termina o valor literal no SQL dinâmico, transformando essa aspa simples final em uma aspa simples de escape incorporada, e a string literal no SQL dinâmico termina com a próxima aspa simples que deveria 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 fazer um dos seguintes:
Não use uma variável local para armazenar o valor "fixo"; basta colocar
REPLACE()
diretamente na criação do Dynamic SQL:O SQL dinâmico não está mais comprometido:
Observações sobre o exemplo Trunction acima:
DELETE tableName
seja destrutivo, mas menos propenso a adicionar um usuário de back-door ou alterar 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 do Open Web Application Security Project (OWASP):
Testing for SQL Injection
Resposta relacionada ao Stack Overflow em SQL Injection e SQL Truncation:
Quão seguro é o T-SQL depois de substituir o caractere de escape?
A questão simples é que você não está confundindo dados com comandos. Os valores dos 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/