Baixe o app para aproveitar ainda mais
Prévia do material em texto
FACULDADE ANHANGUERA – UNIDADE CAMPINAS I TECNOLOGIA EM ANÁLISE E DESENVOLVIMENTO DE SISTEMAS DESENVOLVIMENTO DE SOFTWARE SEGURO – ETAPA 03 ALUNO RA QUEMUEL SANTOS DE AQUINO 8309737322 PAULO VICTOR DE MENEZES 2848233367 SIDMAR PORFÍRIO 7989711257 LEONARDO CUENCAS 7700620660 ATIVIDADE PRÁTICA SUPERVISIONADA (ATPS) TUTOR: PROF. RICARDO AUGUSTO DA SILVA CAMPINAS, 27 DE MAIO DE 2015. SUMÁRIO INTRODUÇÃO .........................................................................................................................................3 O RISCO DE ATAQUES PELA WEB ...........................................................................................................3 UTILIZANDO OS OPERADORES DE CONCATENAÇÃO ..............................................................................8 ATAQUES SQL INJECTION UTILIZANDO LINGUAGENS DINÂMICAS ......................................................22 RELATÓRIO 1 – EVITANDO ATAQUES SQL INJECTION ..........................................................................38 REFERÊNCIAS BIBLIOGRÁFICAS ............................................................................................................40 2 INTRODUÇÃO Esta atividade é importante para que possamos compreender alguns métodos de entrada de dados mal-intencionados e formas de acesso indevido a bancos de dados. Trataremos neste documento sobre codificações abreviadas, mais conhecidas como shortcodes, além de mostrarmos alguns exemplos de sua utilização. Além disso, trataremos também da utilização de códigos dinâmicos. Neste segundo momento, poderemos avaliar a sua utilização em casos que precisamos escrever códigos T-SQL que criam códigos T-SQL mais específicos. O RISCO DE ATAQUES PELA WEB Aplicações web são o principal alvo de ataques de Agentes Maliciosos (hackers ilegais e crackers), devido à possibilidade de alcance rápido de altos ganhos, e ao baixo risco de exposição do criminoso. Dados de cartões de crédito, informações de clientes e de operações da empresa, roubos de identidades e outros dados sigilosos, informações sobre a infraestrutura de dados e vários outros podem ser usados para compor cenários de ataques com alto impacto. Segundo o Instituto Gartner (2009), mais de 75% dos problemas com segurança na internet são devidos a falhas exploráveis a partir das aplicações web. A maior parte das páginas web são naturalmente vulneráveis devido às tecnologias adotadas em sua concepção, à forma como são desenhadas e desenvolvidas, e ao uso de vários objetos e recursos, além da integração de outros sistemas, na medida em que são priorizados os aspectos funcionais que atendem a área de negócios, enquanto os requisitos de segurança ficam em segundo plano. 3 Os ataques podem causar problemas de variados níveis de impacto, como por exemplo: - Interrupção ou queda de desempenho do serviço; - Acesso não autorizado a dados confidenciais e estratégicos; - Roubo de informações cadastrais de Clientes; - Fraudes e modificação de dados no fluxo das operações; - Perdas financeiras diretas e indiretas; - Prejuízos à imagem da marca da empresa; - Perda da lealdade dos Clientes; - Gastos extraordinários com incidentes de segurança; Os riscos de ataques mais comuns são mundialmente conhecidos e podem ser previstos com antecedência, pois são listados pela Open Web Application Security Project (OWASP), e dentre eles, os 3 principais são: 1- SQL Injection; 2- Cross Site scripit; 3- Broken Authentication and Session Management; Mas ataques importantes não são apenas aqueles que causam impacto de imagem, vazamento de informações, ou que afetam a disponibilidade ou desempenho de serviços, pois estes são rapidamente percebidos e provocam imediata reação das áreas de segurança para restaurar a integridade dos sistemas. Os ataques mais graves são aqueles que, quando realizados sobre brechas no ambiente da aplicação web, não serão percebidos de imediato e resultam no acesso a dados sigilosos do negócio, da sua infraestrutura, ou de seus clientes, e que podem ser posteriormente organizados para compor um ataque de impacto mais relevante, ou uma fraude. Considere que da perspectiva da empresa não há como exercer controle total sobre a incidência de todos os fatores que conduzem ao risco, como a identificação prévia do Agente Malicioso, ou do momento do ataque, ou a forma como ele se dará se houver uma vulnerabilidade explorável. 4 Por este motivo, se o fator “vulnerabilidade explorável” for eliminado o quanto antes deste cenário, o ambiente de negócios estará mais seguro e o risco de ataques será minimizado. Este fator pode ser rapidamente administrado a partir da adoção de um processo de Gerenciamento de vulnerabilidades com a metodologia proposta pelo Sistema RedeSegura. - SQL Injection Injeção SQL é um ataque no qual um código mal-intencionado é inserido em cadeias de caracteres que são passadas posteriormente para uma instância do SQL Server para análise e execução. Qualquer procedimento que construa instruções SQL deve ser verificado quanto a vulnerabilidades de injeção porque o SQL Server executará todas as consultas sintaticamente válidas que receber. Mesmo dados com parâmetros podem ser manipulados por um invasor qualificado e determinado. A forma principal de injeção SQL consiste em inserção direta de código em variáveis de entrada de usuário, concatenadas com comandos SQL e executadas. Um ataque menos direto injeta código mal-intencionado em cadeias de caracteres destinadas a armazenamento em uma tabela ou como metadados. Quando as cadeias de caracteres armazenadas são concatenadas subseqüentemente em um comando SQL dinâmico, o código mal-intencionado é executado. O processo de injeção funciona encerrando prematuramente uma cadeia de caracteres de texto e anexando um novo comando. Como o comando inserido pode ter cadeias de caracteres adicionais anexadas a ele antes de ser executado, o malfeitor encerra a cadeia de caracteres injetada com uma marca de comentário "--". O texto subseqüente é ignorado no momento da execução. O script a seguir mostra uma injeção SQL simples. O script cria uma consulta SQL concatenando cadeias de caracteres codificadas com uma cadeia de caracteres inserida pelo usuário: var Shipcity; ShipCity = Request.form ("ShipCity"); var sql = "select * from OrdersTable where ShipCity = '" + ShipCity + "'"; 5 O usuário é solicitado a inserir o nome de uma cidade. Se ele inserir Redmond, a consulta criada pelo script terá a seguinte aparência: SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond' No entanto, suponha que o usuário insira o seguinte: Redmond'; drop table OrdersTable-- Nesse caso, a seguinte consulta é gerada pelo script: SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond';drop table OrdersTable--' O ponto-e-vírgula (;) denota o término de uma consulta e o início de outra. O hífen duplo (--) indica que o restante da linha atual é um comentário e deve ser ignorado. Se o código modificado estiver sintaticamente correto, será executado pelo servidor. Quando o SQL Server processar essa instrução, o SQL Server selecionará primeiro todos os registros em OrdersTable onde ShipCity é Redmond. Em seguida, o SQL Server descartará OrdersTable. Contanto que o código SQL injetado esteja sintaticamente correto, a violação não poderá ser detectada programaticamente. Portanto, é necessário validar todas as entradas de usuário e verificar com cuidado o código que executa comandos SQL construídos no servidor que você está usando. Práticas recomendadas de codificação são descritas nas seções seguintes destetópico. - Dicas de Segurança Sempre valide entrada de usuário testando tipo, comprimento, formato e intervalo. Quando você estiver implementando precauções contra entrada mal-intencionada, considere os cenários de arquitetura e implantação de seu aplicativo. Lembre-se de que programas projetados para executar em um ambiente seguro podem ser copiados para um ambiente sem segurança. As sugestões seguintes devem ser consideradas práticas recomendadas: 6 - Não faça suposições sobre o tamanho, o tipo ou o conteúdo dos dados recebidos pelo seu aplicativo. Por exemplo, você deve fazer a seguinte avaliação: - Como seu aplicativo se comportará se um usuário errôneo ou mal-intencionado inserir um arquivo MPEG de 10 megabytes onde seu aplicativo espera um código postal? - Como seu aplicativo se comportará se uma instrução DROP TABLE for incorporada em um campo de texto? - Teste o tamanho e tipo de dados de entrada e imponha limites apropriados. Isso pode ajudar a impedir excesso deliberado de buffer. - Teste o conteúdo de variáveis de cadeia de caracteres e só aceite valores esperados. Rejeite entradas que contenham dados binários, seqüências de escape e caracteres de comentário. Isso pode ajudar a impedir injeção de script e proteger contra explorações de excesso de buffer. - Quando você estiver trabalhando com documentos XML, valide todos os dados em seu esquema à medida que são inseridos. - Nunca construa instruções Transact-SQL diretamente da entrada do usuário. - Use procedimentos armazenados para validar entrada de usuário. - Em ambientes de várias camadas, todos os dados devem ser validados antes de serem admitidos à zona de confiança. Os dados que não passam pelo processo de validação devem ser rejeitados e um erro deve ser retornado à camada anterior. - Implemente várias camadas de validação. Precauções tomadas contra usuários casualmente mal-intencionados podem ser ineficazes contra determinados invasores. Uma prática mais recomendada é validar a entrada na interface do usuário e em todos os pontos subseqüentes onde ele cruza um limite confiável. 7 Por exemplo, a validação de dados em um aplicativo cliente pode evitar injeção de script simples. No entanto, se a próxima camada assumir que sua entrada já foi validada, qualquer usuário mal-intencionado que possa contornar um cliente poderá ter acesso irrestrito ao sistema. - Nunca concatene entrada de usuário que não seja validada. A concatenação de cadeia de caracteres é o ponto principal de entrada de injeção de script. - Não aceite as seguintes cadeias de caracteres em campos dos quais nomes de arquivos podem ser construídos: AUX, CLOCK$, COM1 a COM8, CON, CONFIG$, LPT1 a LPT8, NUL e PRN. UTILIZANDO OS OPERADORES DE CONCATENAÇÃO A utilização dos operadores de concatenação podem parecer um recurso simples, mas garantem uma maior facilidade de leitura do código e também uma redução aceitável de código. Primeiramente, vejamos com relação a sintaxe para a concatenação que utiliza o operador de adição, em seguida, apresentaremos a sintaxe de utilização do operador de subtração que é bastante semelhante a sintaxe de adição. Estas operações não são encontradas apenas no T-SQL, mas sim em muitas das linguagens de programação conhecidas do mercado, como é o caso do PHP, C, C++, C# ou o próprio Java. - Sintaxe de concatenação com o operador de adição. Expressão += Expressão Ao analisarmos a sintaxe acima, podemos observar que temos duas expressões separadas pela junção dos sinais de adição e de igualdade (+=). Isto é onde a semelhança destes dois operadores termina. O operador de concatenação de Strings é usado para concatenar valores de cadeia. As expressões em que o operador concatenação podem ser de qualquer tipo de dados válido. O valor concatenado que será retornado pela operação de concatenação irá conter o tipo de dados da expressão com a maior precedência. Assim como o suplemento é 8 igual à primeira expressão (do lado esquerdo) que deve ser variável, e é a variável que conterá o resultado final da operação de concatenação. O operador += exige que cada expressão a ser analisada seja de um tipo de dado numérico, exceto BIT. Este operador assume o valor de cada expressão e o adiciona, retornando um valor numérico da operação de adição em que o tipo de dados do resultado tem um tipo de dados que a expressão com a maior precedência. A expressão do lado esquerdo do operador += tem de ser uma variável. Como dito anteriormente, a operação de subtração concatenada será apresentada a sguir, a qual apresenta a sua semelhança. - Sintaxe de concatenação com o operador de subtração. Expressão -= Expressão Assim como a operação utilizando o operador de adição, a string de concatenação com o operador de subtração também possui as duas expressões. Assim como na expressão anterior, ambas as expressões precisam ser de tipos numéricos diferente de BIT. Assim como o operador de +=, o operador de -= assume o valor de cada expressão e subtrai o segundo termo a partir do primeiro e, em seguida, retorna um valor numérico, onde o tipo de dado tem que ser um tipo de dado com a maior precedência. Assim como o operador de +=, o operador de -= requer uma variável para a expressão no lado esquerdo do operador que é igual a subtração, e é esta variável que ficará com o valor resultante da operação. Talvez tenhamos complicado um pouco na teoria com relação ao assunto, mas vamos começar agora com alguns exemplos para um melhor entendimento com relação ao uso desses operadores. Para nossos exemplos estaremos utilizando o SQL SERVER 2014, caso tenham interesse em criar os exemplos, vocês podem baixar através do site http://msdn.microsoft.com/pt-br/evalcenter/dn434042.aspx 9 - Utilizando o operador de concatenação += Começaremos então com um exemplo simples utilizando o operador de adição, onde setaremos duas strings e em seguida, faremos a sua concatenação, o código que estaremos apresentando será de acordo com o apresentado acima. - Utilização simples da concatenação de strings com o operador +. -- Concatenação com shortcodes DECLARE @String varchar(100); SET @String = 'Edson'; SET @String += ' Dionisio'; SELECT @String; GO -- Concatenação sem Shortcode DECLARE @String varchar(100); SET @String = 'Edson'; SET @String = @String + ' Dionisio' SELECT @String; GO Perceba que no código apresentado temos dois exemplos diferentes de como podemos concatenar duas strings juntas. No primeiro conjunto de declarações usamos o nosso atalho (digamos dessa forma) que é o operador de concatenação de String, enquanto que no segundo conjunto de declarações, usamos o operador "+" para concatenar duas strings, de forma bem simples. Como podemos observar, no primeiro conjunto de declarações, só tínhamos especificado a @String uma vez. Ela foi especificada apenas no lado esquerdo do operador +=. Considerando que, no segundo conjunto de instruções, temos a variável @String especificada em ambos os lados do operador de igualdade (=). Esses conjuntos de instruções realizam a mesma operação de concatenação. Mas perceba que utilizando o atalho, +=, 10 reduzimos a quantidade de código escrito e tivemos o mesmo resultado, numa forma mais limpa. - Setando um valor numérico para controle de um loop. No exemplo abaixo vamos mudar um pouco o contexto de strings para valores numéricos e apresentaremos a utilização dos nossos atalhos para incrementar e decrementar valores numéricos. DECLARE @I int =1; WHILE @I < 20 BEGIN SELECT 'O valor da variável @I é ' + CAST(@I as char(1)); SET @I += 1; END Ao olharmos para o código apresentado acima, podemos perceber que incrementamos o valor da variável @I de 1 cada vez que ele passa no while usando o operador de +=. Somar e subtrair valor a uma variável é algo comum quando estamos escrevendo código no nosso dia a dia. Com a adição dos operadores += e -=, esta operação se torna mais fácil de implementar. É isso que ocorre no nosso exemplo, onde temos nossa variável iniciando em 1 e que vai ser concatenada daí em diante até chegar ao valor esperado que é o 20. - Outra maneira de incrementar valores no loop. O código abaixo é equivalente ao código apresentado acima, mas não usa o nosso atalho +=. DECLARE @I int = 1; WHILE @I < 20 BEGIN SELECT 'O valor da variável @I é ' + CAST(@I as char(1)); SET @I = @I + 1; END 11 Ao compararmos os códigos apresentados neste tópico e no anterior, podemos ver que no tópico anterior usamos menos atalhos para incrementar a variável @I. o operador de -= pode ser usado apenas como o operador +=. - Exemplo de utilização do operador -=. No exemplo abaixo contém um código que diminui o valor da variável de controle do loop WHILE usando o operador de subtração. DECLARE @I int = 10; WHILE @I > 0 BEGIN SELECT 'O valor da variável @I é ' + CAST(@I as char(2)); SET @I -= 1; END Em todos os nossos exemplos até este momento usamos uma constante com os operadores de adição ou subtração para aumentar ou diminuir o conjunto da variável. Além disso, também é possível usar as variáveis ou mesmo fórmulas no lado direito destes de ambos os operadores. - Usando uma formula com o operador +=. DECLARE @I int = 3; DECLARE @X int = 15; DECLARE @Y int = 2; SET @I += @X * @Y SELECT @I Se observarmos o código acima, veremos que temos uma formula matemática, sendo esta apresentada com as variáveis X, Y e I, onde realizamos a multiplicação das variáveis @X e @Y e em seguida somamos o valor da variável @I, o que nos dá um resultado de 33 pra @I. 12 - Porquê utilizar T-SQL dinâmicas? Agora que vimos com relação a utilização dos operadores de concatenação, chegou a hora de tratarmos da utilização de T-SQL dinâmicas, saber o porquê de utilizá-las, o porquê de sua importância. A primeira pergunta que surge a nossa mente é: o que são as T-SQL dinâmicas? Por que é necessário utilizá-las? Bem, um T-SQL dinâmico é o código que é potencialmente diferente a cada vez que nós o executamos. Ele é um conjunto de instruções T-SQL que é gerado e executado em tempo de execução. Este código é criado com base em algumas condições ou mesmo parâmetros no bloco de instruções. Quando as "condições ou parâmetros" são diferentes do código T-SQL, ele produz uma T-SQL diferente para ser executada. Geralmente usamos a T-SQL dinâmica quando queremos determinar programaticamente o T- SQL que precisamos com base em parâmetros e/ou com base em dados armazenados em tabelas de banco de dados. Os usos para a T-SQL dinâmica são infinitas. Aqui apresentamos dois exemplos de quando podemos usar uma T-SQL dinâmica, que são quando: - Queremos que o usuário selecione alguns critérios de uma lista suspensa que podem realizar uma consulta para executar de formas diferentes, como uma ordem de classificação. - Sua aplicação não sabe o nome da tabela que poderá ser usada em tempo de execução. Para um melhor entendimento com relação as T-SQL dinâmicas, apresentaremos alguns exemplos a partir desse momento. - Criando uma T-SQL dinâmica simples Para o nosso primeiro exemplo de criação de T-SQL’s dinâmicas, consideremos a seguinte situação: Suponhamos que nós temos uma aplicação onde a interface com o usuário permite que o usuário selecione a tabela que deseja ler a partir de uma lista suspensa. Com isso, podemos ver de início que cada usuário poderá acessar a tabela que quiser para retornar os dados existentes. 13 Para darmos continuidade a esses exemplos, vamos supor que este usuário exiba informações da tabela do banco de dados AdventureWorks2012_database e o mesmo escolha a tabela de dventureWorks2012.Sales.SalesOrderDetail. O código que será apresentado abaixo nos mostra um método de utilizar o código T-SQL dinâmico para retornar os 10 registros da tabela AdventureWorks2012.Sales.SalesOrderDetail. - Exemplo de utilização de uma T-SQL dinâmica simples. -- Declaramos aqui a variável que terá o código da T-SQL dinâmica DECLARE @COMANDO nvarchar(1000); -- Declarando o nome da tabela para leitura DECLARE @Tabela nvarchar(125); SET @Tabela = 'AdventureWorks2012.Sales.SalesOrderDetail'; -- construindo dinamicamente a declaração da T-SQL SET @COMANDO = 'SELECT TOP 10 * FROM ' + @Tabela; -- executando a declaração da T-SQL dinâmica EXECUTE (@COMANDO); O código apresentado acima, disponibiliza a declaração da variável chamada @COMANDO que é para armazenar a instrução SELECT dinamicamente que vai construir a @Table que é para manter o nome da tabela. Então, definiremos a @Table para AdventureWorks2012.Sales.SalesOrderDetail. Para construirmos a nossa declaração T-SQL de forma dinâmica, usamos uma instrução SET. Esta declaração define a @COMANDO para o valor da cadeia concatenada que contém uma instrução SELECT e o valor da @TABLE. Agora que temos a nossa declaração definida, executemos a nossa declaração T-SQL dinâmica que está contida na @COMANDO. 14 Para realizarmos um outro teste com a T-SQL dinâmica, podemos substituir a tabela utilizada por uma tabela diferente no código, alterando apenas a instrução "SET @ Table =" para usarmos a tabela de AdventureWorks2012.Sales.Sales.OrderHeader. - Código dinâmico para exclusão de tabelas Há momentos em que precisamos escrever alguns códigos T-SQL dinâmicos mais complicados. Seja DBA ou programador com privilégios de DBA, nos encontramos em uma daquelas situações em que precisamos gerar um código para executarmos algum tipo de manutenção de banco de dados. O que fazer? O que podemos fazer é construir uma T-SQL dinâmica para fins de manutenção de banco de dados que serão aplicados da seguinte maneira: temos uma visão (view) do sistema e, em seguida, geramos um script que seria executado. Suponhamos então que assumimos a manutenção de um banco de dados e que desejamos excluir várias tabelas de teste que foram criadas em uma determinada base de dados. As tabelas possuem nomes que começam com o prefixo "Teste", como uma boa prática. Para demonstrarmos, vamos ler a view sys.tables e gerar as instruções DELETE apropriadas. -- Criando a base e as tabelas de exemplo USE master; GO CREATE DATABASE QSASOLUCOES; GO USE QSASOLUCOES; GO CREATE TABLE Dados1 (Id int, DataDesc varchar(100)); CREATE TABLE Dados2 (Id int, DataDesc varchar(100)); CREATE TABLE TesteDados1 (Id int, DataDesc varchar(100)); CREATE TABLE TesteDados2 (Id int, DataDesc varchar(100)); GO 15 -- código T-SQL dinâmico para deletar as tabelas USE QSASOLUCOES; GO DECLARE @TabelaNome varchar(100); DECLARE @CMD varchar(1000); SELECT TOP 1 @TabelaNome = nome FROM sys.tables WHERE nome like 'Teste%' ORDER BY nome; WHILE @@ROWCOUNT > 0 BEGIN SELECT @COMAND = 'DROP TABLE ' + @TabelaNome + ';'; PRINT @ COMAND EXECUTE(@COMAND); SELECT TOP 1 @TabelaNome = nome FROM sys.tables WHERE nome like 'Teste%' andnome > @TabelaNome ORDER BY nome; END -- Limpeza profunda! USE master; GO DROP DATABASE QSASOLUCOES; De acordo com o código apresentado acima, temos três seções diferentes. Na primeira seção, criamos um banco de dados chamado QSASOLUCOES, e em seguida criamos quatro tabelas diferentes, duas das quais começam com "Teste". Estas duas tabelas são as tabelas que desejamos apagar com código T-SQL dinamicamente. Na segunda seção apresentada do código está o nosso T-SQL dinâmico. E por fim, na terceira seção do código, limpamos o banco de dados de teste que criamos. Se analisarmos o código apresentado pela segunda seção, encontraremos o código T-SQL dinâmico que primeiro imprime as instruções de DELETE que será executada, em seguida, exclui as tabelas de teste que criamos na primeira parte, onde fazemos o processamento em um loop while para ver as diferentes tabelas que começam com a cadeia de caracteres "Teste". 16 Para cada tabela que começar com "Teste", construímos um comando DELETE que é armazenado no @COMAND. Daí, apresentamos a declaração DELETE usando uma instrução PRINT, imediatamente seguido da execução da instrução usando a instrução EXECUTE. Na última seção, realizamos a limpeza, deixando o banco de dados que criamos. Para testarmos este código, sugerimos que a execução seja realizada a cada seção comentada de forma independente, a fim de iniciarmos a partir de primeira seção. Após executar esta seção, verificamos que existem quatro tabelas no banco de dados QSASOLUCOES. No próximo bloco de código, quando executado, visualizamos duas mensagens sendo exibidas na guia mensagem na janela de Query Analyzer. As duas declarações são mostradas com as duas instruções DELETE que foram geradas e executadas de forma dinâmica. Uma vez que isso é feito, executamos o código da segunda seção, e voltamos a ver as tabelas no nosso banco de dados QSASOLUCOES. Se o Pesquisador de Objetos no SQL Server Management Studio estiver ativo, não esqueçam de atualizar. Alternativamente, podemos selecionar apenas do ponto de vista sys.tables. Agora devemos observar que existem apenas duas tabelas, e as duas tabelas excluídas são aquelas que começam com "Teste". Após essa verificação ter sido realizada, executamos o código presente na terceira seção que é responsável pela limpeza. Este é um exemplo muito simples de como examinar as linhas de metadados e gerarmos a T-SQL dinâmica. Agora o que podemos fazer em casos de sql injection? podemos em algum momento, ter obtido a informação de que uma TSQL dinâmica é ruim, onde a parte ruim da T-SQL dinâmica é que ela abre possibilidades para um ataque de SQL Injection. O SQL Injection é uma técnica de hacking que usuários mal intencionados tentam explorar o uso de um campo de entrada de dados de forma livre. Esses usuários mal intencionados tentam inserir um código T-SQL adicional em um campo de entrada de dados de uma forma diferente de como o campo de entrada de dados foi originalmente destinado a ser utilizado. Através da inserção de códigos T-SQL que podem enganar o sistema e retornar dados que originalmente não deveriam ser obtidos, ou poderiam ainda mais, executar comandos 17 adicionais T-SQL contra seu banco de dados SQL Server. Dependendo das permissões que o aplicativo tenha executado sob um ataque de injeção de SQL, isso pode inserir dados em suas tabelas de banco de dados, excluir uma tabela, ou pior ainda configurar um novo login com direitos sysadmin diferentes. - Base de dados e tabela criadas para teste de sql injection. Para demonstrarmos agora como uma T-SQL dinâmica pode estar sujeita a um ataque de SQL Injection, criaremos uma nova base de dados e uma nova tabela, com o intuito de demonstrar como uma T-SQL dinâmica pode ser vulnerável a um ataque de desse tipo. USE master; GO CREATE DATABASE QSASOLUCOES2; GO USE QSASOLUCOES2; GO CREATE TABLE Produto(ID int, Nome_produto varchar(100), Preco money); INSERT INTO Produto VALUES (1, boneco lego vermelho', 12.99),(2, 'Boneco Hulk vermelho', 23.18), (2, 'Carrinho de fricção', 7.59), (2, 'Drone', 177.76); O código que acabamos de apresentar cria o nosso banco de dados chamado de QSASOLUCOES2 e, em seguida, criamos e preenchemos uma tabela que chamamos de produto com quatro linhas de dados. - Código da stored procedure GetProdutos. Suponhamos que a nossa aplicação tenha uma tela de seleção de dados, onde o usuário poderá inserir uma cadeia de caracteres que está contido em um nome_produto e, em seguida, a aplicação retorne todos os registros da tabela de produtos que contenham a sequência de texto digitado. Após isso, esta sequência de caracteres é processada por uma stored procedure, chamada de GetProdutos, que tem a finalidade de selecionar os dados de acordo com o padrão estabelecido, e, em seguida, estes dados são retornados pela stored procedure e são exibidos para este usuário. 18 CREATE PROC GetProdutos (@pesquisa varchar (100)) AS DECLARE @COMAND varchar(255); SET @COMAND = 'SELECT nome_produto, Preco ' + 'FROM [QSASOLUCOES2].[dbo].Produto ' + 'WHERE nome_produto LIKE ''%' + @ pesquisa + '%'''; PRINT @ COMAND EXEC (@COMAND); Ao analisarmos nossa procedure GetProdutos presente, podemos observar que esta stored procedure aceita um único parâmetro que é o @pesquisa. Este parâmetro é então usado para criar dinamicamente uma instrução T-SQL que é armazenada na varável @COMAND. Para demonstrarmos como usar a nossa stored procedure, vamos então executá-la. - Executando a stored procedure GetProdutos. EXEC GetProdutos 'vermelho'; Ao executarmos essa procedure, obtemos o retorno de duas linhas em nossa consulta que contém o termo “vermelho” contido nelas. Como o código presente em nossa stored procedure GetProdutos tem um parâmetro do tipo varchar e gera a variável @COMAND, isso deixa a stored procedure propícia a um ataque de SQL Injection. Podemos demonstrar isso através da execução da stored procedure GetProdutos com o código apresentado abaixo. - Código que mostra como a stored procedure está vulnerável EXEC GetProdutos 'vermelho%'' and ID = 1 --'; Se pararmos para analisar o código acima, veremos que foi passada uma série de outros termos adicionais para a string "vermelho" ao nosso GetProdutos. Esses termos adicionais que foram passados, nos permitem restringir a consulta para retornar somente os produtos que 19 possuem "vermelho" na coluna nome_produto e que possui um valor de ID de 1. Ao permitirmos que o stored procedure possa usar o texto no parâmetro @pesquisa, permitimos a injeção de caracteres adicionais em que o parâmetro pode fazer com que o código execute outras ações que não foram originalmente destinados a realizar no stored procedure GetProdutos. Até o momento, mostramos um ataque de SQL Injection não destrutivo usando a nossa T- SQL dinâmica com a stored procedure GetProdutos. A maioria dos ataques de SQL Injection busca obter dados adicionais dos nossos sistemas, ou apenas corromper nossos bancos de dados. Para mais informações, vamos explorar um pouco mais através do tópico abaixo. - SQL injection para retornar dados adicionais. EXEC GetProdutos 'vermelho'' ;SELECT * FROM [QSASOLUCOES2].[dbo].Produto;--'; Ao executarmos este código, ele irá gerar os dois conjuntos de resultados, onde o primeiro tem zero linhas e o segundo retorna os registros cadastrados na base anteriormente. Se formos comparar osresultados de uma execução normal da stored procedure GetProdutos encontrada nos resultados com os resultados encontrados da nossa última execução, veremos que o código acima gerou algumas colunas de saída adicionais que nossa stored procedure não foi originalmente preparada para exibir, mas que acabou exibindo devido a um ataque de SQL Injection. Nosso exemplo ainda não era um uso destrutivo de SQL Injection, mas deu brecha para explorarmos o parâmetro @pesquisa de nossa stored procedure getProdutos para retornar dados de todas as colunas da tabela. Para conseguir isso adicionamos o "'; Select * from [QSASOLUCOES].[dbo].produtos; -". Uma observação a se fazer é com relação a adição de dois traços ("-") no fim de nossa string. Isso nos permite comentar o que quer que o código que nossa stored procedure possa ter incluído após o parâmetro. 20 - Código destrutivo de sql injection. Para finalizarmos o exemplo, faremos agora um ataque destrutivo T-SQL. EXEC GetProdutos 'vermelho'' ;DROP TABLE [QSASOLUCOES2].[dbo].Produto;--'; Ao executarmos agora nossa instrução, apagamos a nossa tabela! Esse é um tipo de sql injection destrutivo. Vejamos como podemos combater sql injections. Ninguém quer ter seu código comprometido por um ataque de SQL Injection. Então, para combatermos contra estes ataques de SQL, devemos considerar alguns pontos ao desenvolver o código do aplicativo T- SQL, que são: - Não usar SQL dinâmico; - Editar os parâmetros que o usuário passou para caracteres especiais como o ponto e vírgula e comentários; - Ter parâmetros apenas enquanto for necessário para manter os dados digitados pelo usuário. - Caso devamos usar SQL dinâmico, devemos em seguida usar T-SQL parametrizado que utilize sp_execute sql para executar nossas T-SQL dinâmica, ao invés de usar o EXEC. - Se realmente for necessário a construção do código que contenha T-SQL dinâmico, usar uma T-SQL parametrizada é uma boa maneira de combater o SQL Injection. - Usando parametrizações no T-SQL. ALTER PROC GetProdutos (@pesquisa varchar (100)) AS DECLARE @COMAND nvarchar(1000); DECLARE @ParametroCoringa varchar(102); 21 SET @COMAND = 'SELECT Nome_produto, Preco ' + 'FROM [QSASOLUCOES2].[dbo].Produto ' + 'WHERE Nome_produto LIKE @pesquisa'; SET @ParametroCoringa = '%' + @pesquisa + '%'; EXEC sp_executesql @COMAND,N'@pesquisa varchar(100)',@pesquisa=@ParametroCoringa; Ao fazermos estas alterações, o usuário não poderá mais tentar injetar código T-SQL adicional em nosso stored procedure. ATAQUES SQL INJECTION UTILIZANDO LINGUAGENS DINÂMICAS - Evitando injection em formulário web com PHP Interações entre formulários web e usuários deixam uma porta aberta para inserção de qualquer tipo de dado. Essa possibilidade pode ser ao mesmo tempo o céu ou inferno de um desenvolvedor. Formulários que não possuem controle de dados de entrada correm o sério risco de serem manipulados indevidamente, prejudicando a segurança do sistema. Injections são uma forma de manipulações mal intencionadas de formulários web e podem atingir desde funções em linguagem de programação até manipulações em banco de dados, correndo o risco de danificar o sistema ou expor dados privados. Uma maneira de contornar este problema é tratar todos os dados de entrada, independente de qual tipo de componente de formulário ele teve origem. Como as passagens de dados de uma página para outra é feita através de vetores (POST, GET, SESSION, COOKIE) todos estes vetores devem ser verificados antes dos seus dados serem utilizados. O primeiro passo é definir uma função que elimine toda entrada de formulário considerada maliciosa, que de uma forma ou de outra poderia prejudicar o sistema. As entradas mais comuns são espaços vazios, configuração de cabeçalho de e-mail como Content-Type e bcc, tags HTML como os sinais de maior e menor, e aspas. 22 Para contornar este problema criamos a seguinte função: /** * @abstract Tratamento de injections em formulários. * @return string */ function antiInjection($str) { # Remove palavras suspeitas de injection. $str = preg_replace(sql_regcase("/(\n|\r|%0a|%0d|Content-Type:|bcc:|to:|cc:|Autoreply:|from| select| insert|delete|where|drop table|show tables|#|\*|--|\\\\)/"), "", $str); $str = trim($str); # Remove espaços vazios. $str = strip_tags($str); # Remove tags HTML e PHP. $str = addslashes($str); # Adiciona barras invertidas à uma string. return $str; } Esta função remove possíveis entradas de dados maliciosas. Tem como parâmetro de entrada um vetor, podendo ser qualquer vetor, desde vetores criados pelo próprio desenvolvedor até vetores próprios da linguagem, como POST, GET, COOKIE e SESSION. Com a função de tratamento de injection criada, caímos em outro problema: pode haver vetores dentro de vetores. Um exemplo disto seria um select multiple ou um vetor de checkbox. Para tratar isto criamos outra função: /** * @abstract Antes de tratar os injections, verifica se é vetor ou não. */ function validaParametro($vetor) { if (is_array($vetor)) { foreach ($vetor as $chave => $valor) 23 { if (is_array($valor)) { $vetor[$chave] = validaParametro($valor); } else $vetor[$chave] = antiInjection($valor); } } else $vetor[$chave] = validaParametro($valor); return $vetor; } Dessa forma, antes de tratar possíveis injections, verifica se o parâmetro passado é um vetor ou não, caso seja um vetor, a função validaParametro() é chamada recursivamente passando o novo vetor como parâmetro, caso não seja um vetor, chama a mesma função passando a string a ser tratada. A chamada à função ficaria da seguinte forma: $_POST = validaParametro($_POST); $_GET = validaParametro($_GET); Lembrando que qualquer vetor pode ser verificado. - Prevenindo SQL Injection no ASP.NET A SQL - Structured Query Language - é largamente usada para interagir com banco de dados relacionais. Se você considerar que 90% das aplicações utilizam banco de dados com suporte a SQL vai concluir que o uso da SQL é quase uma unanimidade por ser prática , fácil e portátil. Em se falando de aplicações Web temos uma grande utilização de banco de dados para armazenar as mais diversas informações : endereços e documentos pessoais , contas e valores financeiros , números de cartões de crédito , dados empresariais , etc. Ao colocar sua aplicação na Web você a esta expondo a um acesso mais amplo e indiscriminado. Afinal qualquer um que tenha acesso a url do site terá acesso a sua aplicação e aos dados que ela disponibiliza. 24 Pensando na segurança de suas informações as empresas investem pesado em firewalls , certificação digital e outros recursos , com o objetivo de se proteger de invasores. Para que o controlar o acesso as informações normalmente restringe-se o acesso aos usuários cadastrados usando um nome e senha para identificação ; estes dados são colhidos através de um formulário de login e são então verificados com as informações armazenadas em um banco de dados dos usuários cadastrados; se estiverem corretas o acesso é permitido caso contrário o acesso é negado. É assim que funciona o home banking na internet e uma infinidade de outras aplicações web na qual o acesso é restrito. Você pode ter o aparato mais moderno em termos de tecnologia de segurança protegendo o seu site de um ataque hacker e nem se dar conta de que a vulnerabilidade da sua aplicação esta ali naquele formulário de login. Ele pode ser a porta de entrada para ataques maliciosos atravésda injeção de SQL. A injeção SQL ocorre quando um invasor consegue inserir comandos SQL na instrução SQL que você usa no seu script de modo a burlar a restrição e ter acesso ou danificar as informações armazenadas no seu banco de dados. Neste artigo eu vou mostrar como a injeção de SQL ocorre e falar sobre algumas das medidas que você pode tomar para evitá-la. Embora as informações sejam focadas em páginas ASP e banco de dados SQL Server /Access elas se aplicam a qualquer script e banco de dados que usam um dialeto SQL. - Como ocorre a injeção SQL Se você acha que não deve levar a sério a injeção SQL veja esta notícia da INFO: Segundo o Sans Institute Storm Center, SISC, os ataques atingiram sites de entidades, de empresas e até páginas de governo. “Esse tipo de injeção esvazia o conceito de site ‘confiável’ ou ‘seguro’ ”, diz o pesquisador Donald Smith, do SISC. 25 500 mil sites sofrem injeção de SQL Sexta-feira, 25 de abril de 2008 – 12h54 SÃO PAULO – 500 mil sites foram afetados por uma injeção de SQL que propaga um cavalo-de-tróia ladrão de senhas. Os sites comprometidos com essa injeção de SQL são invadidos por um código que redireciona o browser para outro site, no qual um cavalo-de-tróia ladrão de senhas tenta se instalar na máquina do usuário. O SISC recomenda que as empresas bloqueiem em seus firewalls de borda o acesso ao endereço hxxp:/www.nihaorr1.com e também ao IP a ele associado, 219DOT153DOT46DOT28 veja também esta notícia: http://www.linhadefensiva.org/2008/05/ataques-de-sql-injection-atingem- sites-brasileiros/ 500 mil sites !!!! Você gostaria que o seu estive entre um deles ??? Abaixo temos um típico formulário de login : <form name="frmLogin" action="login.asp" method="post"> Nome : <input type="text" name="nomeUsuario"> Senha: <input type="text" name="senhaUsuario"> <input type="Enviar"> </form> Geralmente quando o usuário clicar no botão - Enviar - o script login.asp será executado para efetuar a validação dos dados informados. Abaixo temos um script típico para um arquivo de validação de informações: <% dim nomeUsuario, senhaUsuario, consulta dim conn, rS nomeUsuario = Request.Form("nomeUsuario") senhaUsuario = Request.Form("senhaUsuario") set conn = server.createObject("ADODB.Connection") set rs = server.createObject("ADODB.Recordset") 26 consulta = "select count(*) from usuarios where nomeUsuario='" & nomeUsuario & "' and senhaUsuario='" & senhaUsuario & "'" conn.Open "Provider=SQLOLEDB; Data Source=(local);Initial Catalog=myDB; User Id=sa; senhaUsuario=" rs.activeConnection = conn rs.open consulta if not rs.eof then response.write "Acesso Concedido" else response.write "Acesso Negado" end if %> Vamos analisar o que ocorre quando um usuário tenta se autenticar. Vamos supor que ele é usuário cadastrado com nome QSASOLUCOES e senha 123456 (só suposição). Ao informar o nome e a senha e clicar no botão Enviar o script do arquivo login.asp será executado. Vamos ver como ficou a instrução SQL montada neste caso : select count(*) from usuarios where nomeUsuario='qsasolucoes' and senhaUsuario='123456' Neste caso o usuário qsasolucoes, senha 123456 será autenticado e terá acesso ao sistema. Tubo bem ? Não , se você usa este tipo de instrução SQL nada esta bem pois o texto final da consulta SQL depende inteiramente do conteúdo das variáveis , e , se o conteúdo destas variáveis não for validado e tratado o texto final concatenado poderá ser um SQL adulterado através de uma injeção SQL. 27 Quer ver ? Vou começar pegando leve. Vamos supor que um hacker decidiu invadir sua página. Uma das primeiras coisas que ele pode fazer é tentar uma injeção SQL , e, vai começar verificando se você esta tratando o apóstrofe (aspa simples: '). Se você não sabe a presença de um caractere de apóstrofe (') no conteúdo de uma variável concatenada no SQL é usada para delimitar strings de texto. Então suponha que ele digite os seguintes dados nos campos nome e senha: nome = tes'te senha = Se você não tratar o apóstrofe vai ocorrer um erro de SQL (incorrect Sintax) ,e , vendo isto o hacker vai ficar mais animado... Agora vou pegar pesado. Suponha então que a seguir ele digite os seguintes dados nome = ' ; drop table users-- senha = Este comando irá excluir a tabela users (se ela existir). E se você pensa que é muito difícil o hacker adivinhar o nome da sua tabela vou mostrar mais abaixo que ele pode fazer isto de uma maneira simples. Isto é possível pois o caractere (;) indica o fim de uma consulta e o começo de outra em T-SQL , e , o caractere (--) no final da linha faz com que o scrpt ASP seja executada sem erro. Continuando o ataque , ele pode também informar o seguinte : nome = admin senha = ' or 1=1-- veja como vai ficar a consulta SQL montada: select count(*) from usuarios where nomeUsuario='admin' and senhaUsuario='' or 1=1--' 28 Aqui a consulta irá verificar se o nome do usuário é admin e se senha é vazio ou 1 for igual a 1 ( o que é verdade) ; bingo, se existir um usuário admin ele entrou no seu sistema. Ele pode também tentar o seguinte : nome = ' or 1=1-- senha = a consulta SQL montada será : select count(*) from usuarios where nomeUsuario='' or 1=1 --' and senhaUsuario='' e bingo, ele burlou o seu sistema. O hacker pode também tentar o seguinte: nome = ' OR "=' senha = ' OR "=' e a consulta SQL montada será : select count(*) from usuarios where nomeUsuario='' OR "=" AND senhaUsuario='' OR "=" a consulta agora esta fazendo a comparação : OR "=" que é sempre verdadeira. Bingo ele entrou no seu site. Para saber o nome das tabelas e campos o hacker pode fazer o seguinte : nome = ' having 1=1-- senha = isto pode causar o seguinte erro: 29 Microsoft OLE DB Provider for ODBC Drivers error '80040e14' [Microsoft] [ODBC SQL Server Driver] [SQL Server] Column 'usuarios.codigo' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause. /login.asp , line 28 e bingo , o hacker agora sabe que o nome da tabela é usuarios e o nome do campo relacionado no formulário como nome é codigo. E então , o que você acha que ele vai fazer ? Fazer a mesma coisa para o campo senha e então ele vai saber o nome da tabela e dos campos relacionados ao formulário. Imagine o estrago que ele não será capaz de fazer agora... Pensa que ele pode ficar somente nisto. Já conhecendo o nome da tabela e das colunas se o hacker quiser saber o tipo de dados do campo ele pode fazer o seguinte : nome = ' UNION SELECT SUM(nomeUsuario) FROM usuarios-- senha = Como o SQL Server vai tentar aplicar a cláusula SUM antes de determinar se o número dos campos nas duas colunas é igual. Ao tentar fazer um SUM em um campo texto o sistema pode emitir a seguinte mensagem de erro: Microsoft OLE DB Provider for ODBC Drivers error '80040e7' [Microsoft] [ODBC SQL Server Driver] [SQL Server] The Sum or average aggregate operation cannot take a varchar data type as na argument. /login.asp , line 109 e bingo de novo, ele agora sabe o tipo de dado do campo nomeUsuario. Agora sabe o que ele vai fazer ? Vai inserir um usuário com nome senha para se logar, assim: 30 nome = ' ; INSERT INTO usuarios VALUES('hacker','111111')-- senha = E... bingo , ele vai se logar como hacker e senha 111111. Acho que com estes exemplos já deu para você perceber que você tem que cuidar com muito mais cuidado das suas instruções SQL .Tenha certeza de uma coisa : as possibilidades do hacker são muitas. - Como evitar uma ataque de injeção SQL A seguir algumas orientações de como vocêpode evitar um ataque de injeção SQL : 1- Estabeleça uma política de segurança rígida e criteriosa limitando o acesso dos seus usuários. Isto quer dizer que você deve dar somente os poderes necessários aos seus usuários. Não de acesso de escrita a tabelas e dê somente acesso as tabelas que o usuário vai precisar. 2- Faça a validação da entrada de dados no formulário e não permita os caracteres inválidos como : (') , (--) e (;) nem de palavras maliciosas como insert , drop , delete, xp_ . Abaixo algumas funções que você pode usar: - Substituindo o apóstrofe(') pelo duplo apóstrofe ('') <% Function ExpurgaApostrofe(texto) ExpurgaApostrofe = replace( texto , "'" , "''") End function %> 31 - Substituindo os caracteres e palavras maliciosas por vazio(""). <% Function LimpaLixo( input ) dim lixo dim textoOK lixo = array ( "select" , "drop" , ";" , "--" , "insert" , "delete" , "xp_") textoOK = input for i = 0 to uBound(lixo) textoOK = replace( textoOK , lixo(i) , "") next LimpaLixo = textoOK end Function %> - Rejeitando os dados maliciosos: <% Function ValidaDados( input ) lixo = array ( "select" , "insert" , "update" , "delete" , "drop" , "--" , "'") ValidaDados = true for i = lBound (lixo) to ubound(llixo) if ( instr(1 , input , lixo(i) , vbtextcompare ) <> 0 ) then ValidaDados = False exit function} end if 32 next end function %> Sempre que puder, rejeite entrada que contenha os caracteres a seguir. Caractere de entrada Significado em Transact-SQL ; Delimitador de consulta. ' Delimitador de cadeia de dados de caractere. -- Delimitador de comentário. /* ... */ Delimitadores de comentário. Texto entre / * e * / não é avaliado pelo servidor. xp_ Usado no início do nome de procedimentos armazenados estendidos de catálogo, como xp_cmdshell. - Limite a entrada de texto para o usuário no formulário de entrada de dados Se o campo nome deve ter somente 10 caracteres restrinja a isto a entrada de dados no formulário. O mesmo vale para a senha; - Faça o tratamento adequado de erros não permitindo que mensagens de erros exponham informações sobre a estrutura dos seus dados; - Faça um log para auditoria dos erros ocorridos e das operações mais importantes da aplicação; - Sempre valide entrada de usuário testando tipo, comprimento, formato e intervalo; - Nunca construa instruções SQL ou Transact-SQL diretamente da entrada do usuário; - Use procedimentos armazenados (stored Procedures) para validar entrada de usuário; - Nunca concatene entrada de usuário que não seja validada. A concatenação de cadeia de caracteres é o ponto principal de entrada de injeção de script; 33 - Teste o conteúdo de variáveis de cadeia de caracteres e só aceite valores esperados. Rejeite entradas que contenham dados binários, seqüências de escape e caracteres de comentário. Isso pode ajudar a impedir injeção de script e proteger contra explorações de excesso de buffer; A Microsoft lançou um kit de ferramentas que pretende auxiliar desenvolvedores e administradores de websites a bloquear e erradicar a recente onda de ataques de SQL Injection (Injeção SQL). As ferramentas, que são voltadas para desenvolvedores web e administradores de TI, buscam auxiliar a identificação de pontos frágeis em scripts ASP que poderiam ser explorados em um ataque de injeção SQL. As ferramentas são: Scrawlr - Examina os arquivos do site e simultaneamente analisa os parâmetros usados em cada página buscando por vulnerabilidades do injeção SQL. Microsoft Source Code Analyzer for SQL Injection - Chamada de MSCASI, esta é uma ferramenta de análise estática para análise dos códigos em ASP. Para executá-la é necessário ter acesso ao código fonte da página. URLScan 3.1 - Esta ferramenta restringe os tipos de solicitações HTTP que o IIS (Internet Information Services) irá processar. Ao bloquear alguns tipos específicos de solicitações, a ferramenta pode prevenir certos ataques. Segurança é coisa séria e vale a pena tomar todas as precauções ao nosso alcance para preservar nossos dados e nossos empregos. - Evitando Ataques em JSP Esta classe de ataques, muito comum em ambientes web, consiste na inserção de comandos nos dados passados como entrada para a aplicação de forma que estes comandos sejam executados pela ou através da aplicação. As subclasses mais comuns são SQL injection e Shell Command Injection. Injeção de SQL, ou SQL Injection, consiste em inserir comandos SQL nos dados enviados para a aplicação de modo a acessar o banco de dados subjacente. Por 34 exemplo, suponhamos que uma aplicação web esteja vulnerável e contenha uma sentença SQL: "SELECT Login, Nome FROM Usuario WHERE Login = '" + sParam + "'". Se o adversário passar para esta aplicação a string "zé' OR '2'='2'", o resultado será: "SELECT Login, Nome FROM Usuario WHERE Login = 'zé' OR '2'='2'". Esta sentença SQL retornará a lista de todos os usuários da aplicação, que poderá ser usada posteriormente como subsídio para outros ataques. O adversário poderia também inserir comandos visando alterar ou apagar dados do banco, causando estragos ainda maiores. A injeção de comandos shell acontece de forma similar se a aplicação acessa o sistema operacional através de um interpretador de comandos. A maneira mais simples de proteger uma aplicação contra injeção de comandos é evitar o uso de interpretadores externos, como um interpretador SQL ou um shell. Para evitar o uso de comandos de shell, devemos usar as bibliotecas do sistema que realizem as funções específicas necessárias. Nos casos em que não há alternativa, tais como as chamadas a interpretadores SQL de bancos de dados, devemos validar criteriosamente os dados recebidos e certificar que estes dados não incluam nenhum comando ou conteúdo potencialmente nocivo. Devemos também estruturar as requisições de maneira que os parâmetros recebidos sejam efetivamente tratados como dados e não como comandos. No caso de Java, pode-se empregar também stored procedures e prepared statements (representados em Java pelas classes CallableStatement, quando são usadas stored procedures, e PreparedStatement, quando se usa SQL) para aumentar o nível de proteção à informação. O exemplo abaixo ilustra o uso incorreto de PreparedStatement, pois insere os dados recebidos pela aplicação diretamente no comando SQL que será enviado para o banco de dados. String name = request.getParameter("name"); PreparedStatement pstmt = conn.prepareStatement("insert into EMP (ENAME) values ('" + name + "')"); pstmt.execute(); pstmt.close(); 35 A maneira correta seria: PreparedStatement pstmt = conn.prepareStatement ("insert into EMP (ENAME) values (?)"); String name = request.getParameter("name"); pstmt.setString (1, name); pstmt.execute(); pstmt.close(); No segundo exemplo, o parâmetro name é tratado exclusivamente como dado (mais especificamente uma string), o que protege a aplicação contra a tentativa de injeção de código SQL. Abaixo, temos um trecho de código onde o CallableStatement é usado de forma incorreta: String name = request.getParameter("name"); String sql = "begin ? := GetPostalCode('" + name + "'); end;"; CallableStatement cs = conn.prepareCall(sql); cs.registerOutParameter(1, Types.CHAR); cs.executeUpdate(); String result = cs.getString(1); cs.close(); No código acima, há a possibilidade que o código SQL enviado ao servidor seja (o texto em vermelho correspondeao suposto conteúdo do parâmetro name): begin ? := GetPostalCode(''); delete from users; commit; dummy(''); end; A maneira correta de usar o CallableStatement está mostrada abaixo. É possível perceber que há o registro do parâmetro name, como dado contendo caracteres. String name = request.getParameter("name"); CallableStatement cs = conn.prepareCall ("begin ? := 36 GetStatePostalCode(?); end;"); cs.registerOutParameter(1,Types.CHAR); cs.setString(2, name); cs.executeUpdate(); String result = cs.getString(1); cs.close(); Nos casos em que é usado o framework Hibernate, as manipulações de dados mais simples ou corriqueiras são tratadas diretamente pelo framework. No entanto, o Hibernate disponibiliza a linguagem HQL (ou Hibernate Query Language) para que os programadores possam definir consultas ou manipulações complexas dos dados da aplicação. Embora o HQL apresente algumas restrições no conteúdo dos comandos que diminuam o risco de ataques de injeção de comandos, os riscos não são totalmente eliminados. Assim, são necessários os mesmos cuidados que no caso do uso direto de SQL para acesso à base de dados. O exemplo a seguir apresenta um comando HQL passível de ser atacado por injeção de código, através do parâmetro paymentIds: Payment payment = (Payment) session.find("from com.example.Payment as payment where payment.id = " + paymentIds.get(i)); O mesmo trecho pode ser reescrito usando outras versões da função session.find que permitem definir explicitamente os tipos dos dados a serem passados para a base de dados. Desta forma, o risco de ataques por injeção de comandos é bastante reduzido, conforme demonstra o exemplo abaixo: String pId = paymentIds.get(i); TsPayment payment = (TsPayment) session.find("from com.example.Payment as payment where payment.id = ?", pId, StringType); 37 RELATÓRIO 1 - EVITANDO ATAQUES SQL INJECTION O usuário com que o SGBD é executado dentro do sistema operacional também é um ponto a ser observado. Utilização de usuários como root (UNIX) e administrator (Windows) fazem com que eventuais comandos executados maliciosamente no sistema operacional através de uma injeção de SQL rodem com privilégios irrestritos no sistema. O grande vilão responsável pela imensa maioria das vulnerabilidades de injeção de SQL é, sem sombra de dúvidas a concatenação de strings para montagem de comandos SQL. No entanto, é perfeitamente viável a construção de consultas dinâmicas sem a concatenação direta dos parâmetros à string principal. A API JDBC, por exemplo permite a utilização de prepared statements para execução de acessos ao SGBD. Um prepared statement é uma instrução SQL pré-processada pela API do SGBD, que recebe os parâmetros separadamente da string de consulta principal. Na string principal, os parâmetros constam como marcadores que serão, posteriormente, substituídos pelo SGBD pelos seus respectivos valores durante a execução da consulta. SELECT nome FROM funcionario WHERE departamento = ? Parâmetro 1: nomeDepartamento Neste exemplo, o valor da variável nomeDepartamento será utilizado exclusivamente como critério de comparação com a coluna departamento. Caso seja recebido como valor de nomeDepartamento a string “ '' OR 1 = 1”, o resultado será a busca de um registro cujo valor de departamento seja a string “ '' OR 1 = 1”, garantindo assim o sentido original da consulta. Evidentemente, é claro, as APIs e mecanismos dos SGBDs que fornecem funcionalidades como os prepared statements citados acima necessitam, internamente, serem seguras contra a injeção de SQL. A implementação insegura das camadas de acesso a 38 dados pode tornar uma aplicação que as utilize completamente insegura, mesmo que esta tenha sido construída sob os mais criteriosos preceitos de programação segura. A utilização de sistemas de bancos de dados como repositórios de informações é padrão na imensa maioria dos sistemas de informações existentes hoje, inclusive naqueles acessíveis via web. Na mesma medida em que benefícios são adquiridos pela sua utilização, também os riscos de ataques como a injeção de SQL são trazidos à tona. Há um grande foco de atenção na área de segurança computacional em torno de algoritmos de criptografia e verificações de vulnerabilidades em sistemas operacionais, SGBDs e servidores de aplicações. No entanto, as grandes brechas de segurança que viabilizam a injeção de SQL estão presentes no código da própria aplicação, e não nos softwares de infra-estrutura que esta utiliza. É imprescindível, portanto, que o mesmo rigor com que são avaliados os aspectos de segurança dos softwares acessórios à aplicação quanto à sua segurança, seja aplicado também na avaliação da própria aplicação. É fato que as boas práticas de programação que restringem imensamente o potencial de ocorrência de um ataque de injeção de SQL são em linhas gerais bastante simples de serem aplicadas, mas também é fato que, a existência de um único ponto do código de uma aplicação construído de forma insegura torna a aplicação insegura como um todo. Faz-se de suma importância neste cenário, que seja incluído no currículo de todos os cursos formadores de mão-de-obra especializada no desenvolvimento de sistemas computacionais, a compreensão da natureza deste tipo de ataque, como ele é possível e as práticas adotadas no dia-a-dia da construção de sistemas para evitá-los. Neste sentido, este trabalho espera dar sua contribuição, através da demonstração das formas de ataque como motivadores de conscientização da imprescindibilidade da adoção de práticas seguras de construção e configuração de sistemas aqui relatadas. 39 REFERÊNCIAS BIBLIOGRÁFICAS [1] ANLEY, C. (more) Advanced SQL Injection, 2002. Disponível em <www.ngssoftware.com/papers/more_advanced_sql_injection.pdf>. Acesso em Nov 2009. [2] BAMBNEK, J. SQL Injection Worm on the Loose, 2008. Disponível em <http://isc.sans.org/diary.html?storyid=4393>. Acesso em Nov 2009. [3] BEAULIEU, A; TRESELER, M. E. Learning SQL. 2ª ed. Sebastapol, CA, EUA: O'Reilly, 2009. [4] BLINDFOLDED SQL Injection, 2009. Disponível em <http://www.imperva.com/download.asp?id=4>. Acesso em Nov 2009. [5] CHAPPLE, M. Testing For SQL Injection Vulnerabilities, 2008. Disponível em <http://databases.about.com/od/security/a/sql_inject_test.htm>. Acesso em Nov 2009. [6] CLARKE, J. SQL Injection Attacks and Defense. 1ª ed. [S.l.]: Syngress, 2009. [7] CUMMING, A.; RUSSEL, G. SQL Hacks. 1ª ed. [S.l.]: O'Reilly Media, 2006. [8] DATE, C. J. Introdução a Sistemas de Banco de Dados. Rio de Janeiro: Campus, 2000. [9] HOWARD, M.; LEBLANC, D. Writing secure code, 2nd ed. Redmond, 2003. [10] LEMONIAS, N. Introduction to Buffer Overflows, 2009. Disponível em <http://www.packetstormsecurity.org/papers/attack/Memory_Exploitation_Res_Publication.p df>. Acesso em Nov 2009. [11] MEEK, B. L.; HEATH, P. Guide to Good Programming Practice. 1ª ed. [S.l.]: Ellis Horwood Ltd, 1980. 40 [12] MEIER J. D. et al. Improving Web Application Security: Threats and Countermeasures, 1ª ed. Microsoft Press. Redmond, 2003. [13] ORACLE/PLSQL: Oracle System Tables, 2009. Disponível em <http://www.techonthenet.com/oracle/sys_tables/index.php>. Acesso em Nov 2009. [14] SCHLICHTING, D. SQL Server 2005 System Tables and Views, 2005. Disponível em <http://www.databasejournal.com/features/mssql/article.php/3508881/SQL-Server-2005-System-Tables-and- Views.htm>. Acesso em Nov 2009. 41 SUMÁRIO O RISCO DE ATAQUES PELA WEB
Compartilhar