Baixe o app para aproveitar ainda mais
Prévia do material em texto
Teste de Segurança Nos módulos anteriores aprendemos como especificar requisitos de segurança, elaborar a arquitetura segura e implementar o software com segurança. Neste módulo veremos uma etapa importante do ciclo de desenvolvimento seguro, que é a fase de testes de segurança. Os testes de segurança têm como objetivo validar a presença e verificar a efetividade dos controles de segurança implementados no software. Neste módulo serão abordados os diferentes tipos de testes de segurança, o modo como são conduzidos e as equipes envolvidas em cada atividade. 1 Testes de software Dentre os testes de software mais comuns entre as equipes de desenvolvimento estão os testes funcionais, de recuperação e performance. Os testes funcionais abrangem os testes unitários, de integração, lógicos e de regressão. Todos esses testes são muito importantes, porém eles devem ser complementados com outros tipos de testes de forma a garantir a qualidade e a segurança do software. A figura 5.1 mostra os tipos de teste que serão descritos neste módulo. Testes Funcionais Os testes funcionais têm como objetivo assegurar que o software atende às expectativas do usuário, ou seja, estão de acordo com os requisitos especificados. Eles podem ser divididos em testes unitários, integração, lógico e regressão. Unitário: é o primeiro teste a ser conduzido para verificar se o software funciona corretamente. Ele é realizado pelo desenvolvedor durante a fase de implementação. Cada parte do software é testada isoladamente para verificação de erros de build, de compilação ou da lógica de programação. Este tipo de teste é realizado mais facilmente quando a arquitetura do software é modular, com alta coesão e baixo acoplamento. Os benefícios dos testes funcionais incluem: validar a lógica funcional; encontrar complexidades, ineficiências, e vulnerabilidades no código (credenciais explícitas, etc.) alta cobertura de testes 2 Metodologias de Teste de Segurança Existem duas abordagens principais para se descobrir vulnerabilidades em uma aplicação: testes de caixa-branca (white box) e testes de caixa-preta (black box). Os fatores que separam estas abordagens são: o nível de conhecimento da aplicaçãoe os recursos disponíveis no momento em que os testes são realizados. Constituem recursos para a execução dos testes: o código-fonte da aplicação, a documentação, as bibliotecas que ela utiliza e, em alguns casos, o próprio desenvolvedor da aplicação. 2.1 Caixa Branca Os testes de caixa branca são conhecidos também como análise de código, pois o código é o principal recurso utilizado durante os testes. Esses testes podem ser conduzidos assim que o desenvolvedor produza algum código. Porém, o momento ideal para sua execução é em conjunto com os testes unitários ou com os testes de integração. As entradas para os testes de caixa branca incluem a documentação de arquitetura, o código-fonte, arquivos de configuração, casos de uso e de abuso, os arquivos de configuração e os requisitos de segurança do sistema. Por meio dos testes é possível verificar os erros relacionados à lógica interna do software. É possível detectar problemas embutidos no código, tais como: trojans, bombas lógicas, spyware e backdoors implementados intencionalmente por funcionários internos. Durante a execução dos testes serão analisados os fluxos de informação e fluxos de controle, as interfaces, as entradas e saídas de dados. A figura 5.2 mostra o fluxo do teste de caixa branca. Como podemos ver na figura 5.2, os testes de caixa branca são capazes de detectar defeitos ou bugs no código, falhas na arquitetura do software ou desvio de padrões arquiteturais previamente definidos. A partir desses apontamentos o desenvolvedor corrige o código-fonte. Em um processo de análise de código descentralizada, em que os testes são conduzidos por uma equipe diferente da de desenvolvimento, os responsáveis pelos testes podem abrir requisições de mudança e elaborar recomendações de correção. A vantagem dos testes de caixa branca é a cobertura ou abrangência que consegue atingir. De posse do código-fonte, é possível analisar todos fluxos de execução do programa. A desvantagem é a alta complexidade, pois as ferramentas de revisão de código geralmente costumam gerar muitos falsos positivos, ou seja, situação em que a vulnerabilidade apontada pela ferramenta é improcedente. Com isso é necessário que o desenvolvedor ou a equipe de testes avalie todos os resultados gerados pela ferramenta. Outra desvantagem é a baixa viabilidade, pois nem sempre é possível ter acesso ao código-fonte da aplicação. Listas de ferramentas para análise de código podem ser acessadas em: http://samate.nist.gov/index.php/Source_Code_Security_Analyzers.html http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis 2.2 Caixa Preta Os testes de caixa preta são o oposto dos de caixa branca. A equipe de testes tem poucos recursos disponíveis durante os testes de caixa preta, ficando praticamente limitadas ao que o usuário final pode observar da aplicação. Ou seja, para as aplicações web, o recurso disponível é somente a conectividade com a aplicação. É importante observar que a aplicação deve estar em execução para se realizar os testes de caixa preta, ao contrário dos testes de caixa branca. Assim, os testes de caixa preta consistem em uma análise comportamental do software, enquanto os de caixa branca consistem em análises estruturais. Existem dois momentos indicados para a realização dos testes de caixa preta: antes de colocar a aplicação em produção ou com a aplicação em produção. A execução dos testes antes do software entrar em produção tem como objetivo identificar e corrigir as vulnerabilidades de forma pró-ativa, minimizando os riscos de segurança. Outra vantagem de testar antes de entrar em produção é que o custo de correção é menor se comparado aos testes conduzidos após o software ter entrado em produção. Por sua vez, a execução dos testes após o software entrar em produção tem a vantagem de cobrir os problemas existentes no ambiente real da aplicação. Essa lacuna pode ser minimizada fazendo com que o ambiente de testes antes do software entrar em produção seja o mais semelhante possível ao ambiente de produção, sendo essa a abordagem mais recomendada. Dentre as vantagens dos testes de caixa-preta podemos citar a viabilidade e a simplicidade. Os testes de caixa preta são mais viáveis do ponto de vista de precisar de menos artefatos para a condução dos testes. Todas aquelas entradas para os testes de caixa branca são reduzidas apenas a um requisito: conectividade com a aplicação. A simplicidade é explicada pelo fato de ser exigido pouco (ou nenhum) conhecimento da lógica da aplicação para a execução dos testes de caixa preta. Porém, há uma desvantagem na metodologia de testes de caixa preta em relação à cobertura, pois é mais difícil atingir todas as partes da aplicação. Outra metodologia de testes encontrada na literatura é a de caixa cinza 1 , que é um meio termo entre caixa branca e caixa preta.. O teste é realizado a partir do que o usuário final pode observar da aplicação, porém o código-fonte ou a documentação arquitetural está disponível para direcionar a execução dos testes. 2.3 Diferenças entre caixa branca e caixa preta Como vimos nos tópicos anteriores, os testes de segurança podem ser conduzidos por meio de uma metodologia de caixa branca ou caixa preta. Cada uma delas possui suas vantagens e desvantagens que foram mostradas por alto. Esta seção trata com mais detalhes as diferenças entre as duasmetodologias. Os testes de caixa branca podem ser executados mais cedo no processo de desenvolvimento seguro, fazendo com que a segurança seja integrada ao código. As vulnerabilidades detectadas durante os testes de caixa branca têm maior precisão, podem até mesmo apontar a linha de código onde se encontra o problema, ajudando a identificar as causas do problema. Já os testes de caixa preta fornecem uma visão melhor da capacidade do software ser realmente explorado, mostrando os efeitos de um ataque. Entre as limitações dos testes de caixa preta estão a exatidão da causa raiz da vulnerabilidade e a cobertura dos testes. É importante entender que mesmo os testes de caixa branca têm suas limitações quanto à cobertura dos testes: as dependências (serviços, bibliotecas, etc.) ou os componentes de terceiros podem não ser cobertos pelos testes por falta do código-fonte. A tabela 5.1 sintetiza as principais diferenças entre as duas metodologias. Tabela 5.1 Caixa branca Caixa preta foco do teste estrutura comportamento identificação das vulnerabilidades podem identificar a linha exata da vulnerabilidade podem identificar os efeitos da vulnerabilidade cobertura alta limitada detecção de falhas lógicas alta baixa detecção de problemas do ambiente de produção limitada alta 2.4 Fuzzing Outra metodologia de testes de segurança é o Fuzzing 1 , também conhecido como teste de injeção de falhas. Fuzzing consiste em um teste de força bruta em que entradas inesperadas (aleatórias ou pseudoaleatórias) são submetidas ao software e o seu comportamento é observado. O objetivo dos testes de fuzzing é disparar um comportamento inseguro ou inesperado no software por meio da entrada de dados. Ele é capaz de indicar a efetividade dos mecanismos de validação de entrada. O fuzzing pode ser usado tanto para testar aplicações quanto para testar protocolos e formatos de arquivos. Dentre as falhas identificadas por meio dos testes de fuzzing estão: buffer overflows, execução de código, exceções não tratadas e DoS. Os endereços a seguir mostram dois exemplos reais de vulnerabilidades que poderiam ter sido identificadas pelos testes de fuzzing: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-0599 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-1788 Os testes de fuzzing podem ser divididos em duas técnicas diferentes: dumb fuzzing e smart fuzzing. Quando os dados de entrada que serão usados nos testes forem realmente aleatórios, sem nenhuma relação com o software, protocolo ou arquivo que será analisado, a técnica utilizada é a dumb fuzzing. Quando os dados de entrada são gerados a partir de uma estrutura já conhecida do software, protocolo ou formato de arquivo, a técnica utilizada é a smart fuzzing. Um exemplo de estrutura que poderia ser utilizada para geração dos dados de entrada pode ser encontrada em: http://www.mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm 2.5 Escaneamentos de vulnerabilidades e testes de invasão Duas técnicas comuns de testes de caixa preta é o escaneamento de vulnerabilidades e os testes de invasão, também conhecidos como testes de penetração (penetration tests). Uma diferença fundamental entre essas duas técnicas consiste no modo de operação: o escaneamento de vulnerabilidades age de modo passivo; os testes de invasão agem de forma ativa. O modo passivo é usado para detectar a presença de vulnerabilidades que possam ser exploradas por um atacante. O modo ativo vai um passo além e tem como objetivo verificar se o software pode ser comprometido, explorando as vulnerabilidades identificadas. Em outras palavras, o escaneamento de vulnerabilidades identifica partes do software que podem ser atacadas, enquanto os testes de invasão medem a resiliência do software realizando ataques reais nas vulnerabilidades identificadas. As informações produzidas pelo escaneamento de vulnerabilidades são úteis para: mapear o ecosistema computacional, a rede e as interfaces da aplicação; identificar versões dos servidores de aplicação, as portas abertas e os serviços ativos; identificar patches a serem aplicados; identificar vulnerabilidades. Os testes de invasão podem produzir informações úteis para: fornecer uma visão mais apurada da segurança dos softwares; servir de referências para ações corretivas; determinar controles de segurança que devem ser aplicados para conter as vulnerabilidades identificadas. Os testes de invasão geralmente são divididos nas seguintes etapas: reconhecimento; exploração; remoção de evidências; relatório e recomendações. Durante a fase de reconhecimento serão utilizadas várias técnicas de enumeração do alvo. Esta primeira fase é responsável por fornecer uma descrição ampla da aplicação que será testada (versões, interfaces, portas abertas, vulnerabilidades, etc). A segunda fase, exploração, consiste em tentar atacar o alvo por meio de força bruta das credenciais de autenticação, escalação de privilégios, descobrimento de informações sensíveis, manipulação da base de dados, criação de contas, etc. A terceira fase consiste em retornar o sistema ao estado inicial, removendo todas as alterações que foram feitas. A quarta fase é a geração do relatório, que deve incluir as recomendações técnicas de tratamento das vulnerabilidades e também as informações de não conformidade com a política de segurança da organização. 3 Testes de Segurança em Software Nesta seção abordaremos os diferentes tipos de testes e como eles devem ser realizados. O objetivo é tratar adequadamente a segurança do código que está sendo desenvolvido na fase de implementação. Portanto, antes de realizar os testes, é importante esclarecer o seguinte questionamento: "O software a ser testado é novo ou é decorrente de uma nova versão (release)?" Caso seja uma nova versão, então deverá ser validado se o atual estado de segurança não regrediu em relação à versão anterior. Este tipo de validação poderá ser realizado através de testes de regressão, tendo como enfoque os aspectos de segurança. 3.1 Mecanismo de validação de entradas A maioria das vulnerabilidades de segurança podem ser mitigadas por meio da validação adequada das entradas de dados. Ações maliciosas tais como: buffer overflow, injeções de código e scripting podem ser efetivamente reduzidas caso o software realize a validação das entradas antes de efetuar o processamento. Em um ambiente cliente/servidor, rotinas de validação de entrada devem ser utilizadas tanto no cliente quanto no servidor. Rotinas de validação de entrada de dados no lado cliente são geralmente utilizadas para verificar questões de performance e usabilidade, pois pouco contribuem para a segurança. Os recursos de implementação das rotinas de validação de entrada de dados devem ser direcionados para efetuarem as checagens de segurança do lado servidor. Atributos das entradas de dados incluem: os intervalos; formatos; tipos e valores de dados. Todos eles devem ser verificados pela rotina de validação. Quando esses atributos forem mapeados, então os testes de segurança direcionados para o mecanismo de validação de entrada poderão ser realizados utilizando técnicas de reconhecimento de padrões via expressão regular e/ou técnicas de fuzzing. Os testes devem ser conduzidos para assegurar que o mecanismo de validação de entrada baseado em whitelist (lista de padrões aceitáveis) é permitido enquanto o controle de entrada baseado em blacklist (assinatura de entradas não aceitáveis) está realizando o bloqueio adequadamente. Os testes não devem somentetratar das rotinas de validação (white list e black list), mas devem também incluir cenários de testes para verificação dos mecanismos proteção contra a dissimulação da entrada de dados, pois caso o atacante consiga usar um encoding diferente do previsto pelas listas de validação, este não seria enquadrado pelas regras de validação e provavelmente obteria êxito no ataque. Os ataques para burlar os mecanismos de validação de dados evoluem basicamente de duas formas: por meio da "descoberta" de variações dos ataques existentes; e pela elaboração de novos ataques, à medida que a tecnologia evolui (AJAX, XML, HTML 5, etc). O que ocorre com maior frequencia é a variação de um mesmo ataque de maneira que eles passem despercebidos pelos mecanismos de filtragem. Uma das técnicas é aplicar encoding de caracteres distintos (BASE 64, URL encoding, Hex encoding, UTF 8, etc). Por exemplo, abaixo são mostradas 3 variações de JavaScript que produzem o mesmo resultado: alert(document.cookie) alert(document['cookie']) with(document)alert(cookie) Um exemplo de novas formas de ataque surge com o avanço da tecnologia. Por exemplo, o HTML 5 permitirá atributos em tags de fechamento: </a onmousemove="alert(1)"> Por ultimo, o exemplo de variações na representação de dados inclui as várias formas de representar o sinal "menor que" < < PA== 0x3C %253C %%33%63 CAHR(60) Por meio da técnica de variações dos ataques é possível burlar os filtros ModSecurity, PHP-IDS, XSS-Filter do Internet Explorer 8 e o plugin do Firefox NoScript . 3.2 Teste de defesa para injeção de código Ataques de injeção decorrem das entradas de dados fornecidas pelo usuário que, por sua vez, são interpretadas como um comando ou parte de um comando. A validação da entrada de dados é uma salvaguarda efetiva em relação às falhas de injeção. Para efetuar os testes de validação de entrada, é fundamental determinar as origens das entradas de dados e dos eventos, os quais irão disparar ações no servidor de aplicação, na base de dados ou no sistema operacional. As entradas de dados podem ter como origem formulários de autenticação, campos de busca, campos do tipo hidden em páginas web, querystrings em endereços de URLs, cookies, etc. Uma vez que a origem é determinada, testes de validação de entrada podem ser utilizados para assegurar que o software não é suscetível aos ataques de injeção de código. Para que a cobertura dos testes em relação às falhas de injeção seja mais abrangente, existem outros testes que precisam ser executados para assegurar que: Queries parametrizadas não são suscetíveis a injeção e estão sendo utilizadas corretamente; A construção de queries dinâmicas sem validação dos dados não é permitida; Mensagens de erro e exceções são explicitamente tratadas de forma adequada; Precedures e declarações não essenciais são removidas do banco de dados; Erros gerados pelo SGBD não revelam a estrutura interna do banco de dados; Estruturação de listas do tipo white list que somente permitem a entrada de dados no formato, tipo, faixa de valores e tamanho adequados. Por fim, é possível aplicar testes de caixa branca, caixa preta ou fuzzing para validar os mecanismos de controle. Os testes de caixa branca serão aplicados para verificar se todas as funções de validação estão implementadas de forma correta. O responsável pelos testes pode verificar se o código está em conformidade com os controles padrões da organização, por exemplo, o uso de determinada biblioteca. Os testes de caixa preta ou de fuzzing são aplicados de forma a submeter vetores de ataques à aplicação para tentar burlar os controles de segurança. 3.3 Teste de defesa para o não repúdio O não repúdio pode ser tratado pelo gerenciamento e auditoria de sessões. Casos de teste devem validar as trilhas de auditoria de forma precisa, tendo em vista a determinação dos atores e suas respectivas ações. As trilhas de auditoria devem ser definidas de forma adequada por meio dos casos de abuso do sistema. Os testes de segurança devem verificar se uma determinada ação é única, está protegida e é rastreável. Os casos de testes devem também incluir a verificação dos procedimentos de proteção e gerenciamento das trilhas de auditoria e também a integridade dos registros de auditoria (logs). Inclui-se também nestes testes a verificação do uso adequado dos certificados digitais. A publicação NIST SP 800-92 fornece diretrizes para a proteção das trilhas de auditoria e gerenciamento seguro dos arquivos de log. 3.4 Teste de defesa para o Spoofing Casos de testes para verificar a possibilidade de spoofing precisam ser estruturados. Ataques de spoofing no nível de rede incluem o address resolution protocol (ARP) poisoning, spoofing de endereço IP e spoofing de endereço media access control (MAC). Em relação à camada de aplicação, há testes de spoofing de usuários e certificados, phishing e a verificação de código que possa permitir a personificação indevida de outras identidades. A estruturação de testes para verificação de spoofing de usuário e/ou certificado em conjunto com a validação da presença de mecanismos de proteção na camada de transporte podem garantir uma comunicação segura e proteção para ataques do tipo man- in-the-middle. Os casos de teste devem também contemplar a expiração de cookies e a presença de criptografia nos mesmos. 3.5 Teste de falha de software O software tende a falhar quando ocorre um erro acidental do usuário ou mesmo um ataque intencional. Os seguintes erros de projeto de software são geralmente a causa das falhas nas aplicações: deficiências na elicitação dos requisitos, omissão arquitetural e/ou erros de implementação. Testes de falha de software devem incluir a verificação dos seguintes princípios de segurança: Falha segura (fail safe): verificar a confidencialidade, integridade e disponibilidade do software ou dos dados que são manipulados quando ocorrem falhas. Atenção especial deve ser dada para verificar a integridade de qualquer processo de autenticação, de modo que a ocorrência de uma falha não resulte em um estado inseguro (atacante autenticado no sistema). Também deve estar contemplado nos casos de teste o funcionamento adequado do mecanismo de bloqueio das contas de usuários. Tratamento de erros e exceções: verificar o tratamento de erros e exceções, que deve incluir casos de teste para validar as mensagens de erro e também mecanismos de encapsulamento dos seus detalhamentos. Os testes devem induzir a ocorrência de falhas para verificar se nenhuma informação sigilosa ou detalhes de implementação estão sendo exibidos. 3.6 Teste de validação do mecanismo de criptografia Os testes de validação do mecanismo de criptografia devem atestar: Conformidade com padrões de mercado: confirmar a utilização dos padrões de mercado nos módulos criptográficos, tais como os descritos no documento e-Ping. É importante utilizar algoritmos reconhecidamente seguros tais como: AES, RSA e DSA. O NIST FIPS 140-2 disponibiliza uma suíte de conformidade que possibilita a aferição de quatro níveis de segurança. Validação de ambiente: o ambiente computacional no qual ocorrem as operações criptográficas também deve ser validado. A norma ISO/IEC 15408 common criteria (CC) pode ser utilizada para esta finalidade. Validação dos dados: os dados não validados em ambientes e sistemas criptográficos não estão protegidos como um todo e devem ser considerados como se estivessem em texto claro. A proteção de dados sensíveis, privados e pessoais que utilizam mecanismos de proteção criptográficos devem ser validados, tendo comoobjetivo a efetividade da confidencialidade. Implementação criptográfica: As sementes utilizadas pelos algoritmos criptográficos devem ser checadas para que tenham um grau adequado de aleatoriedade. Testes de caixa branca permitem verificar se as chaves criptográficas não estão implementadas no código-fonte em texto claro, pois caso estejam implementadas no código-fonte, representam uma grande fragilidade do sistema criptográfico. 3.7 Teste de defesa para Buffer Overflow As consequências de um ataque de buffer overflow podem ser devastadoras, portanto é necessário testar as defesas contra este tipo de ataque. A vulnerabilidade de buffer overflow pode ser verificada através das técnicas de caixa preta e caixa branca. Os testes de caixa preta podem ser conduzidos utilizando técnicas de fuzzing. Já os testes de caixa branca devem incluir os seguintes casos de teste: Todas as entradas de dados estão sendo tratadas, inclusive em relação ao tamanho máximo permitido para a entrada dos dados; É realizada a checagem dos limites de alocação de memória; A conversão de um tipo de dado para outro é realizada explicitamente; APIs banidas e inseguras não estão sendo utilizadas; O código compilado utiliza proteções em nível de compilação que protegem a pilha e/ou atribui de forma aleatória o espaço de memória. 3.8 Teste de defesa para escalonamento de privilégios Testes para verificação de cenários de escalonamento de privilégio devem ser conduzidos para que sejam evitadas ocorrências de usuários ou processos que obtêm acesso a recursos aos quais originalmente não deveriam ter. O escalonamento de privilégio pode ser do tipo vertical, horizontal ou híbrido. O escalonamento de privilégio vertical permite que um determinado usuário ou processo acesse de forma não autorizada recursos que necessitariam de um perfil de acesso maior. Já o tipo horizontal permite o acesso de forma não autorizada a recursos que necessitam de um perfil de acesso semelhante. A referência insegura a objetos consiste em uma falha arquitetural e/ou de implementação que geralmente permite o escalonamento de privilégio. Portanto a verificação da manipulação de parâmetros precisa ser realizada, tendo como objetivo a garantia de que o privilégio associado a um determinado perfil não pode ser escalonado. 4 Documentação de Vulnerabilidades e Rastreabilidade As falhas de segurança encontradas durante os testes devem ser priorizadas levando em consideração a facilidade de exploração, a exposição e o impacto causado por um ataque. O ideal é que as vulnerabilidades sejam corrigidas antes que o software entre em produção, embora isto nem sempre é possível. O importante é que o desenvolvedor receba um relato completo da vulnerabilidade para que ele possa entender e corrigir o problema de forma efetiva. O objetivo do relato de vulnerabilidades é mostrar de forma clara o problema e fazer com que alguém assuma a responsabilidade de tratá-lo. Alguns campos comuns nos relatórios de vulnerabilidades incluem: identificador da vulnerabilidade: um número único que identifique a vulnerabilidade para facilitar a comunicação entre as partes interessadas; título: é o nome dado à vulnerabilidade (por exemplo: upload de arquivo malicioso); descrição: consiste em um resumo do problema; detalhes passo a passo: é uma forma de mostrar a reprodução da vulnerabilidade. É importante descrever como a vulnerabilidade funciona para que o desenvolvedor entenda a lógica do problema. Uma abordagem é a descrição passo a passo da exploração ou então por meio de telas (screenshots); responsável pela análise: o responsável pela análise pode sanar eventuais dúvidas sobre a vulnerabilidade; responsável pela correção: o responsável pela correção pode informar o estado da correção (por exemplo: resolvida, em andamento, etc); data do relato: é importante, para fins de registro, colocar a data em que o relato foi descoberto e divulgado; severidade: a severidade do problema é importante para embasar decisões de priorização das correções. Durante o relato de vulnerabilidades é importante que as informações sensíveis sejam mascaradas. Por exemplo, em uma vulnerabilidade de injeção de código em que é possível extrair a tabela de senhas do banco de dados é necessário ocultá-las durante o detalhe passo a passo da vulnerabilidade. Outro exemplo é uma tela de erro que mostra informações confidenciais, onde as informações devem ser suprimidas no relato da vulnerabilidade.
Compartilhar