Os parâmetros são realmente suficientes para evitar injeções Sql?

83

Tenho pregado para meus colegas e aqui no SO sobre a vantagem de usar parâmetros em consultas SQL, especialmente em aplicativos .NET. Cheguei até a prometer que eles forneceriam imunidade contra ataques de injeção de SQL.

Mas estou começando a me perguntar se isso realmente é verdade. Há algum ataque de injeção de SQL conhecido que terá êxito em uma consulta parametrizada? Você pode, por exemplo, enviar uma string que causa um estouro de buffer no servidor?

É claro que há outras considerações a serem feitas para garantir que um aplicativo da web seja seguro (como limpar a entrada do usuário e tudo mais), mas agora estou pensando em injeções de SQL. Estou especialmente interessado em ataques contra MsSQL 2005 e 2008, uma vez que são meus bancos de dados principais, mas todos os bancos de dados são interessantes.

Edit: Para esclarecer o que quero dizer com parâmetros e consultas parametrizadas. Ao usar parâmetros, quero dizer usar "variáveis" em vez de construir a consulta sql em uma string.
Então, em vez de fazer isso:

SELECT * FROM Table WHERE Name = 'a name'

Nós fazemos isso:

SELECT * FROM Table WHERE Name = @Name

e, em seguida, defina o valor do parâmetro @Name no objeto de consulta / comando.

Rune Grimstad
fonte
devemos esclarecer o que se entende por parâmetros (como Jonathan Leffler apontou) - Eu estava pensando em parâmetros de procedimento armazenado, mas também existem? parms and {0} parms ...
Steven A. Lowe
É muito mais fácil dizer, não usamos concatenação para construir uma consulta.
Como a tag é asp.net, presumo que você esteja criando aplicativos da web. Nesse caso, você também deve cuidar de ataques XSS e talvez de outros
Spikolynn

Respostas:

50

Espaços reservados são suficientes para prevenir as injeções. Você ainda pode estar aberto a estouros de buffer, mas esse é um tipo de ataque completamente diferente de uma injeção de SQL (o vetor de ataque não seria sintaxe SQL, mas binário). Uma vez que todos os parâmetros passados ​​terão escape adequado, não há como um invasor passar dados que serão tratados como SQL "ao vivo".

Você não pode usar funções dentro de marcadores de posição e não pode usar marcadores de posição como nomes de coluna ou tabela, porque eles são escapados e citados como literais de string.

No entanto, se você usar parâmetros como parte de uma concatenação de string dentro de sua consulta dinâmica, ainda estará vulnerável à injeção, porque suas strings não terão escape, mas serão literais. Usar outros tipos de parâmetros (como inteiro) é seguro.

Dito isso, se você estiver usando a entrada de uso para definir o valor de algo como security_level, então alguém poderia simplesmente tornar-se administrador em seu sistema e ter um free-for-all. Mas isso é apenas validação de entrada básica e não tem nada a ver com injeção de SQL.

Adam Bellaire
fonte
O ponto chave é entender a questão levantada pela resposta de Steve Lowe, também apontada no artigo @mikekidder cita - você deve estar atento onde quer que o SQL dinâmico esteja, seja no aplicativo ou no servidor. SQL dinâmico é perigoso - mas pode ser tornado seguro.
Jonathan Leffler
“não há como um invasor passar dados que serão tratados como SQL 'ao vivo'”. - Isso não é bem verdade, veja os exemplos abaixo.
Booji Boy de
Todos os exemplos abaixo definem "consulta parametrizada" para significar parâmetros de aceitação de código SQL. A definição normal é uma consulta que usa sua coleção de parâmetros do DBMS. Exceto um bug de DBMS, esta última técnica impede a injeção de SQL.
HTTP 410 de
2
Eu li todos os links. Cite qualquer link que se refira a um ataque de injeção funcional contra a coleção de parâmetros do DBMS. Na verdade, o link que você postou refere-se especificamente a essa abordagem como derrotando a injeção de SQL (consulte a seção "Usando parâmetros SQL de segurança de tipo").
HTTP 410 de
Oi! Você poderia fornecer um link para a gramática Oracle SQL ou algo parecido para provar essa resposta. Eu entendo e concordo totalmente com você, mas seria ótimo ter um link oficial para documentação, gramática etc. BestRegards, Raimbek
Raimbek Rakhimbek
13

Não, ainda há risco de injeção de SQL sempre que você interpola dados não validados em uma consulta SQL.

Os parâmetros de consulta ajudam a evitar esse risco, separando os valores literais da sintaxe SQL.

'SELECT * FROM mytable WHERE colname = ?'

Tudo bem, mas há outros propósitos de interpolação de dados em uma consulta SQL dinâmica que não pode usar parâmetros de consulta, porque não é um valor SQL, mas um nome de tabela, nome de coluna, expressão ou alguma outra sintaxe.

'SELECT * FROM ' + @tablename + ' WHERE colname IN (' + @comma_list + ')'
' ORDER BY ' + @colname'

Não importa se você está usando procedimentos armazenados ou executando consultas SQL dinâmicas diretamente do código do aplicativo. O risco ainda existe.

A solução nesses casos é empregar o FIEO conforme necessário:

  • Filtro de entrada: valide se os dados parecem inteiros legítimos, nomes de tabelas, nomes de colunas, etc. antes de interpolar.

  • Saída de escape: neste caso, "saída" significa colocar dados em uma consulta SQL. Usamos funções para transformar variáveis ​​usadas como literais de string em uma expressão SQL, para que as aspas e outros caracteres especiais dentro da string sejam escapados. Devemos também usar funções para transformar variáveis ​​que seriam usadas como nomes de tabelas, nomes de colunas, etc. Quanto a outras sintaxes, como escrever expressões SQL inteiras de forma dinâmica, esse é um problema mais complexo.

Bill Karwin
fonte
12

Parece haver alguma confusão neste tópico sobre a definição de uma "consulta parametrizada".

  • SQL, como um proc armazenado que aceita parâmetros.
  • SQL que é chamado usando a coleção DBMS Parameters.

Dada a definição anterior, muitos dos links mostram ataques que funcionam.

Mas a definição "normal" é a última. Dada essa definição, não conheço nenhum ataque de injeção de SQL que funcione. Isso não significa que não haja um, mas ainda não vi.

A partir dos comentários, não estou me expressando com clareza suficiente, então aqui está um exemplo que espero ser mais claro:

Esta abordagem está aberta para injeção de SQL

exec dbo.MyStoredProc 'DodgyText'

Esta abordagem não está aberta para injeção de SQL

using (SqlCommand cmd = new SqlCommand("dbo.MyStoredProc", testConnection))
{
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter newParam = new SqlParameter(paramName, SqlDbType.Varchar);
    newParam.Value = "DodgyText";
    .....
    cmd.Parameters.Add(newParam);
    .....
    cmd.ExecuteNonQuery();
}
HTTP 410
fonte
Você pode esclarecer o que você quer dizer com coleção de parâmetros do DBMS, em oposição a um procedimento que aceita parâmetros?
Rune Grimstad de
Rune, leia a seção "Use Type-Safe SQL Parameters" deste link: msdn.microsoft.com/en-us/library/ms161953.aspx
HTTP 410
Minha resposta foi à pergunta original de Rune, antes de ser editada com a atualização.
mikekidder
Eu li e reli aquele artigo msdn sobre injeção de sql e ainda não vejo como há uma diferença entre os parâmetros que um procedimento armazenado leva e os parâmetros que uma consulta dinâmica leva. Além do fato de que as consultas dinâmicas são dinâmicas. Você ainda precisa vincular os parâmetros, certo?
Rune Grimstad
É a ligação que faz a diferença. Se você chamar um procedimento armazenado com parâmetros diretamente, nenhuma filtragem de entrada será feita. Mas se você vincular (por exemplo) usando a coleção de parâmetros SqlCommand no .NET, todos os parâmetros serão filtrados e tratados como texto simples.
HTTP 410
10

qualquer parâmetro sql do tipo string (varchar, nvarchar, etc) que é usado para construir uma consulta dinâmica ainda é vulnerável

caso contrário, a conversão do tipo de parâmetro (por exemplo, para int, decimal, data, etc.) deve eliminar qualquer tentativa de injetar sql por meio do parâmetro

EDIT: um exemplo, onde o parâmetro @ p1 se destina a ser um nome de tabela

create procedure dbo.uspBeAfraidBeVeryAfraid ( @p1 varchar(64) ) 
AS
    SET NOCOUNT ON
    declare @sql varchar(512)
    set @sql = 'select * from ' + @p1
    exec(@sql)
GO

Se @ p1 for selecionado em uma lista suspensa, é um possível vetor de ataque de injeção de sql;

Se @ p1 for formulado de forma programática sem a capacidade do usuário de intervir, então não é um vetor de ataque de injeção sql potencial

Steven A. Lowe
fonte
Não; a questão toda é que a string passada para o DBMS não faz parte da instrução SQL. Portanto, o valor na string não faz diferença para a interpretação do SQL - apenas para os valores referenciados pelo SQL.
Jonathan Leffler
É assim que vejo os parâmetros também. Eles deveriam prevenir este problema.
Rune Grimstad de
2
Steven está certo se, por exemplo, você está passando uma string para um sp que a usa para executar algo como sp_executeSql (servidor sql), então você ainda tem o risco de injeção de sql.
alexmac de
@Steven: isso não é um parâmetro para o SQL; você teria que ter um espaço reservado (ponto de interrogação) no lugar da concatenação da string. E o SQL não permite que você especifique o nome da tabela por espaço reservado. Essa é uma vulnerabilidade de injeção SQL pura - o problema original.
Jonathan Leffler
@Steven: talvez o termo 'parâmetro' tenha sido sobrecarregado com muita frequência. : D
Jonathan Leffler
6

Um estouro de buffer não é injeção de SQL.

Consultas parametrizadas garantem que você está seguro contra injeção de SQL. Eles não garantem que não haja possíveis exploits na forma de bugs em seu servidor SQL, mas nada garante isso.

Blorgbeard está fora
fonte
2

Seus dados não estarão seguros se você usar sql dinâmico de qualquer forma ou formato porque as permissões devem estar no nível da tabela. Sim, você limitou o tipo e a quantidade de ataque de injeção dessa consulta em particular, mas não limitou o acesso que um usuário pode obter se encontrar uma maneira de entrar no sistema e você estiver totalmente sujeito a usuários internos acessando o que não deveriam para cometer fraude ou roubar informações pessoais para vender. SQL dinâmico de qualquer tipo é uma prática perigosa. Se você usar procs armazenados não dinâmicos, você pode definir permissões no nível do procedimento e nenhum usuário pode fazer nada exceto o que é definido pelos procs (exceto administradores de sistema, é claro).

HLGEM
fonte
portanto, a lição aqui é que se você deve usar sql dinâmico, faça-o apenas dentro de um procedimento armazenado. 1 bom conselho!
Steven A. Lowe
1
Não - SQL dinâmico em procs armazenados ainda pode introduzir falhas de injeção de SQL, interpolando dados não validados na consulta dinâmica.
Bill Karwin de
Não, a lição aqui é nunca usar SQl dinâmico
HLGEM
@HLGEM - certo, e automóveis estão envolvidos em acidentes de trânsito, portanto, nunca devemos usar automóveis.
Bill Karwin de
Mas o SQL dinâmico em um procedimento armazenado é executado (por padrão) com a permissão do chamador, não como o SQL estático que é executado com a permissão do proprietário do proc armazenado. Esta é uma distinção importante.
HTTP 410 de
1

É possível que um procedimento armazenado seja vulnerável a tipos especiais de injeção SQL por meio de estouro / truncamento, consulte: Injeção habilitada por truncamento de dados aqui:

http://msdn.microsoft.com/en-us/library/ms161953.aspx

Booji Boy
fonte
Se você ler o artigo em detalhes, verá que o uso da coleção Parameters do SQL Server evita esse ataque. E essa é a definição normal de uma "consulta parametrizada" - ela usa a coleção de parâmetros do DBMS.
HTTP 410 de
1

Lembre-se de que, com os parâmetros, você pode armazenar facilmente a string ou dizer o nome de usuário se não tiver nenhuma política, "); eliminar usuários da tabela; -"

Isso por si só não causará nenhum dano, mas você sabe melhor onde e como essa data é usada posteriormente em seu aplicativo (por exemplo, armazenado em um cookie, recuperado posteriormente para fazer outras coisas.

nos
fonte
1

Você pode executar sql dinâmico como exemplo

DECLARE @SQL NVARCHAR(4000);
DECLARE @ParameterDefinition NVARCHAR(4000);

SELECT  @ParameterDefinition = '@date varchar(10)'

SET @SQL='Select CAST(@date AS DATETIME) Date'

EXEC sp_executeSQL @SQL,@ParameterDefinition,@date='04/15/2011'
Mohamed Abbas
fonte