Percebo que as consultas SQL parametrizadas são a maneira ideal de higienizar a entrada do usuário ao criar consultas que contenham entrada do usuário, mas estou me perguntando o que há de errado em receber entradas do usuário e escapar de aspas simples e cercar toda a cadeia de caracteres com aspas simples. Aqui está o código:
sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"
As aspas simples inseridas pelo usuário são substituídas por aspas duplas, o que elimina a capacidade do usuário de finalizar a sequência, para que qualquer outra coisa que eles digitem, como ponto-e-vírgula, sinais de porcentagem etc. fará parte da sequência e não é realmente executado como parte do comando.
Estamos usando o Microsoft SQL Server 2000, para o qual acredito que as aspas simples são o único delimitador de strings e a única maneira de escapar do delimitador de strings, portanto, não há como executar nada que o usuário digitar.
Não vejo nenhuma maneira de iniciar um ataque de injeção de SQL contra isso, mas percebo que, se fosse tão à prova de balas quanto me parece, alguém já teria pensado nisso e seria uma prática comum.
O que há de errado com este código? Existe uma maneira de obter um ataque de injeção de SQL além dessa técnica de higienização? Exemplo de entrada do usuário que explora essa técnica seria muito útil.
ATUALIZAR:
Ainda não conheço nenhuma maneira de iniciar efetivamente um ataque de injeção de SQL contra esse código. Algumas pessoas sugeriram que uma barra invertida escaparia de uma aspas simples e deixaria a outra finalizar a string para que o restante da string fosse executado como parte do comando SQL, e eu percebo que esse método funcionaria para injetar SQL no um banco de dados MySQL, mas no SQL Server 2000 a única maneira (que eu consegui encontrar) de escapar de uma aspas simples é com outra aspas simples; barras invertidas não o farão.
E, a menos que haja uma maneira de interromper o escape da aspas simples, nada do restante da entrada do usuário será executado porque tudo será considerado como uma sequência contígua.
Entendo que existem maneiras melhores de higienizar as entradas, mas estou realmente mais interessado em saber por que o método que forneci acima não funcionará. Se alguém souber de alguma maneira específica de montar um ataque de injeção SQL contra esse método de higienização, eu adoraria vê-lo.
Respostas:
Primeiro de tudo, é apenas uma prática ruim. A validação de entrada é sempre necessária, mas também é sempre duvidosa.
Pior ainda, a validação da lista negra é sempre problemática, é muito melhor definir explícita e estritamente quais valores / formatos você aceita. É certo que isso nem sempre é possível - mas, em certa medida, sempre deve ser feito.
Alguns trabalhos de pesquisa sobre o assunto:
A questão é que qualquer lista negra que você faça (e listas de permissões muito permissivas) pode ser ignorada. O último link para o meu artigo mostra situações em que até mesmo as aspas podem ser contornadas.
Mesmo que essas situações não se apliquem a você, ainda é uma má idéia. Além disso, a menos que seu aplicativo seja trivialmente pequeno, você terá que lidar com manutenção e, talvez, com uma certa quantidade de governança: como garantir que tudo seja feito corretamente, em todos os lugares o tempo todo?
A maneira correta de fazer isso:
fonte
sp_executesql
vez deEXEC
). Ou seja, você pode gerar dinamicamente sua instrução SQL, desde que nenhum texto concatenado venha do usuário. Isso também traz benefícios de desempenho;sp_executesql
suporta cache.Ok, esta resposta está relacionada à atualização da pergunta:
Agora, além da fuga de barra invertida do MySQL - e levando em consideração que estamos falando sobre o MSSQL, existem três maneiras possíveis de o SQL ainda injetar seu código
Leve em consideração que nem todos serão válidos o tempo todo e dependem muito do seu código real:
Então, o que você obtém - é o nome de usuário, escapou e, em seguida, aparou para 20 caracteres. O problema aqui - colocarei minha citação no 20º caractere (por exemplo, depois dos 19 anos), e sua citação de escape será cortada (no 21º caractere). Então o SQL
combinado com o nome de usuário malformado acima mencionado, a senha já estará fora das cotações e conterá apenas a carga diretamente.
3. Contrabando de Unicode - Em certas situações, é possível transmitir um caractere de alto nível unicode que se parece com uma citação, mas não é - até chegar ao banco de dados, onde de repente está . Como não é uma citação quando você a validar, será fácil ... Consulte minha resposta anterior para obter mais detalhes e link para a pesquisa original.
fonte
Em poucas palavras: nunca faça uma consulta escapando a si mesma. Você é obrigado a entender algo errado. Em vez disso, use consultas parametrizadas ou, se não puder fazer isso por algum motivo, use uma biblioteca existente que faça isso por você. Não há razão para fazer você mesmo.
fonte
Sei que isso passou muito tempo depois que a pergunta foi feita, mas ..
Uma maneira de iniciar um ataque ao procedimento 'citar o argumento' é com o truncamento de strings. De acordo com o MSDN, no SQL Server 2000 SP4 (e no SQL Server 2005 SP1), uma seqüência muito longa será silenciosamente truncada.
Quando você cita uma sequência, ela aumenta de tamanho. Todo apóstrofo é repetido. Isso pode ser usado para enviar partes do SQL para fora do buffer. Assim, você pode efetivamente aparar partes de uma cláusula where.
Isso provavelmente seria útil principalmente em um cenário de página 'user admin', no qual você poderia abusar da instrução 'update' para não fazer todas as verificações que deveria fazer.
Portanto, se você decidir citar todos os argumentos, saiba o que acontece com os tamanhos das strings e verifique se você não fica truncado.
Eu recomendaria ir com parâmetros. Sempre. Só queria poder aplicar isso no banco de dados. E, como efeito colateral, é mais provável que você obtenha melhores hits do cache, porque mais instruções parecem iguais. (Isso certamente era verdade no Oracle 8)
fonte
Eu usei essa técnica ao lidar com a funcionalidade de 'pesquisa avançada', onde a criação de uma consulta a partir do zero era a única resposta viável. (Exemplo: permita ao usuário procurar produtos com base em um conjunto ilimitado de restrições nos atributos do produto, exibindo colunas e seus valores permitidos como controles da GUI para reduzir o limite de aprendizado para os usuários.)
Por si só, é seguro AFAIK. Como outro respondente apontou, no entanto, talvez você também precise lidar com o escape de backspace (embora não seja ao passar a consulta para o SQL Server usando ADO ou ADO.NET, pelo menos - não pode garantir todos os bancos de dados ou tecnologias).
O problema é que você realmente precisa ter certeza de quais sequências contêm entrada do usuário (sempre potencialmente maliciosas) e quais são consultas SQL válidas. Uma das armadilhas é se você usar valores do banco de dados - esses valores foram originalmente fornecidos pelo usuário? Nesse caso, eles também devem ser escapados. Minha resposta é tentar limpar o mais tarde possível (mas não mais tarde!), Ao construir a consulta SQL.
No entanto, na maioria dos casos, a ligação de parâmetros é o caminho a seguir - é apenas mais simples.
fonte
Saneamento de entrada não é algo que você queira fazer pela metade. Use todo o seu rabo. Use expressões regulares nos campos de texto. TenteCast seus numéricos para o tipo numérico apropriado e relate um erro de validação se não funcionar. É muito fácil procurar padrões de ataque em sua entrada, como '-. Suponha que toda a entrada do usuário seja hostil.
fonte
flavors
nessa página).De qualquer maneira, é uma má ideia, como você parece saber.
Que tal algo como escapar da citação em uma string como esta: \ '
Sua substituição resultaria em: \ ''
Se a barra invertida escapar da primeira cotação, a segunda cotação encerrará a sequência.
fonte
Resposta simples: Funcionará às vezes, mas não o tempo todo. Você deseja usar a validação da lista branca em tudo o que faz, mas sei que isso nem sempre é possível, então você é forçado a seguir a melhor lista negra de palpites. Da mesma forma, você deseja usar procs armazenados parametrizados em tudo , mas mais uma vez, isso nem sempre é possível, então você é forçado a usar sp_execute com parâmetros.
Existem maneiras de contornar qualquer lista negra utilizável que você possa criar (e algumas listas brancas também).
Um artigo decente está aqui: http://www.owasp.org/index.php/Top_10_2007-A2
Se você precisar fazer isso como uma solução rápida, a fim de dar tempo para obter uma solução real, faça-o. Mas não pense que você está seguro.
fonte
Existem duas maneiras de fazer isso, sem exceções, para proteger-se das injeções de SQL; declarações preparadas ou procedimentos armazenados pré-determinados.
fonte
Se você tiver consultas parametrizadas disponíveis, deverá usá-las o tempo todo. Basta uma consulta deslizar pela rede e seu banco de dados está em risco.
fonte
Sim, isso deve funcionar até que alguém execute SET QUOTED_IDENTIFIER OFF e use aspas duplas em você.
Editar: não é tão simples como não permitir que o usuário mal-intencionado desative os identificadores citados:
Existem várias maneiras de ativar o QUOTED_IDENTIFIER sem você necessariamente saber. É certo que essa não é a arma que você procura, mas é uma superfície de ataque bastante grande. Claro, se você também escapou de aspas duplas - então estamos de volta onde começamos. ;)
fonte
Sua defesa falharia se:
(no último caso, teria que ser algo que foi expandido somente após a substituição)
fonte
Patrick, você está adicionando aspas simples em TODAS as entradas, até mesmo entradas numéricas? Se você tiver entrada numérica, mas não estiver colocando aspas simples, poderá fazer uma exposição.
fonte
Que código feio seria todo esse sanitização da entrada do usuário! Em seguida, o StringBuilder desajeitado para a instrução SQL. O método de instrução preparado resulta em um código muito mais limpo, e os benefícios da Injeção SQL são uma adição muito boa.
Também por que reinventar a roda?
fonte
Em vez de alterar uma única citação para (como parece) duas aspas simples, por que não alterá-lo para um apóstrofo, uma citação ou removê-lo completamente?
De qualquer forma, é meio que um desdém ... especialmente quando você legitimamente tem coisas (como nomes) que podem usar aspas simples ...
NOTA: Seu método também pressupõe que todos os que trabalham no seu aplicativo sempre se lembrem de higienizar as entradas antes que elas atinjam o banco de dados, o que provavelmente não é realista na maioria das vezes.
fonte
Embora você possa encontrar uma solução que funcione para seqüências de caracteres, para predicados numéricos, você também precisa garantir que eles estejam apenas passando números (verificação simples: é possível analisá-la como int / double / decimal?).
É muito trabalho extra.
fonte
Pode funcionar, mas parece um pouco chato para mim. Eu recomendaria verificar se cada string é válida, testando-a contra uma expressão regular.
fonte
Sim, você pode, se ...
Depois de estudar o tópico, acho que a entrada higienizada como você sugeriu é segura, mas apenas sob estas regras:
você nunca permite que os valores de string provenientes dos usuários se tornem algo além de literais de string (ou seja, evite dar a opção de configuração: "Digite nomes / expressões adicionais da coluna SQL aqui:"). Tipos de valor diferentes de cadeias (números, datas, ...): converta-os em seus tipos de dados nativos e forneça uma rotina para literal SQL de cada tipo de dados.
você usa
nvarchar
/nchar
columns (e literalmente as sequências de prefixo comN
) OU valores-limite que entram emvarchar
/char
columns somente para caracteres ASCII (por exemplo, lançam uma exceção ao criar a instrução SQL)você sempre valida o comprimento do valor para caber no comprimento real da coluna (lance uma exceção se for mais longa)
você garante que
SET QUOTED_IDENTIFIER
está sempreON
Cumprindo estes 4 pontos, você deve estar seguro. Se você violar algum deles, será aberta uma maneira de injeção de SQL.
fonte