Artigo - Hábitos seguros no desenvolvimento de software
Transcrição
Artigo - Hábitos seguros no desenvolvimento de software
CENTRO UNIVERSITÁRIO DE BRASÍLIA - UniCEUB Faculdade de Ciências Exatas e Tecnologia - FAET Curso de Bacharelado em Ciência da Computação Disciplina: Segurança e Auditoria de Sistemas Professor: Maurício Rocha Lyra HÁBITOS SEGUROS: 8 REGRAS SIMPLES PARA O DESENVOLVIMENTO DE CÓDIGO MAIS SEGURO Michael Howard Eu tive muita sorte em trabalhar com milhares de bons desenvolvedores durante muitos anos, que queriam aprender a escrever software mais seguros. Durante esse tempo, também aprendi muito com pessoas que são muito boas na criação de sistemas seguros e isso me fez pensar. Imaginei se haveria habilidades ou hábitos comuns que os "desenvolvedores de segurança" compartilhavam. E acontece que a resposta foi um ressonante sim! Este artigo apresenta a lista de hábitos compartilhados pelos desenvolvedores de código seguro. Agora, uma coisa de que tenho certeza é que qualquer pessoa que for revisar a lista imediatamente notará a ausência de alguns hábitos. Não há problema. Eu sei que existem outras ótimas idéias por aí. Esta é minha lista! Então, dito isso, seguem meus exemplos de hábitos que observei ao longo dos anos. Hábito nº 1: Assumir responsabilidade Essa é uma variação do comentário clássico "There is no silver bullet" (Não existe uma solução mágica) feito há mais de 25 anos por Fred Brookes em The Mythical Man Month. Obter a segurança correta em seu produto depende totalmente de você. Ninguém mais, e certamente nenhuma ferramenta mágica ou linguagem de programação, resolve todos os problemas de segurança. Não me entenda mal. Eu gosto de ferramentas de análise de código-fonte, mas elas não consertarão todas as vulnerabilidades de segurança como em um passe de mágica. Só você pode fazer isso. Os produtos seguros são criados pelos desenvolvedores que criam designs seguros e escrevem códigos seguros. Finalmente, escrever códigos é um esforço individual. Você é um indivíduo e não pode ser substituído por uma ferramenta. Portanto, a segurança de seu produto é nossa responsabilidade! Os worms Blaster e CodeRed exploram códigos que foram escritos por indivíduos (veja a Figura 1). Figura 1 Código vulnerável escrito por indivíduos Lembre-se de que todos os códigos serão escrutinados e possivelmente atacados. Isso é absolutamente normal. Ser atacado faz parte. A grande questão é: seu código estará comprometido? Só você pode determinar esse resultado. Por isso, fique orgulhoso de seu código. Você deve ficar satisfeito com a qualidade de seu código e conseguir dormir à noite sabendo que se ele for atacado, você fez tudo o que era possível para evitar que ele fosse derrotado. Se for possível, peça que um colega especialista em segurança revise seu código. Não deixe que alguém que não conheça nada sobre segurança examine seu código e espere que essa pessoa encontre bugs de segurança e vulnerabilidades. Vá atrás de alguém que realmente entenda do assunto para analisar seu código. E, não seja tão prepotente, de modo a não pedir ajuda quando precisa dela. Eu mencionei que você não deve confiar exclusivamente nas ferramentas, mas deve, sem dúvida, aproveitar alguma que esteja disponível. Execute todas as ferramentas de análise de código-fonte em seu código e as execute com freqüência. Aproveite todas as elaborações de linguagem defensiva e truques de biblioteca possíveis disponíveis. Por exemplo, no C#, envolva o código referente à rede que executa acesso a matrizes, em que o índice da matriz é derivado de uma solicitação de rede, em operadores de verificação para detectar possíveis erros aritméticos em inteiros. Hábito nº 2: Nunca confiar nos dados Eu já disse isso bilhões de vezes e vou dizer novamente: toda entrada é “do mal” até que se prove o contrário. Se você olhar as vulnerabilidades de segurança mais odiosas, todas compartilham a característica comum de o desenvolvedor ter confiado nos dados de entrada. O problema é que se o código assumir que os dados são bem formados, o que acontecerá se suas suposições forem incorretas? Em um dia bom, seu aplicativo provavelmente será interrompido. Em um dia ruim, o invasor poderia injetar código malicioso em seu processo e destruí-lo. A definição quase excêntrica de um sistema seguro é aquele que executa as tarefas que devem ser executadas e nada mais. Mas, quando há questões de confiança de entrada, você geralmente pode conseguir com que o sistema execute outras tarefas. Uma análise superficial dos dados de CVE (vulnerabilidades e exposições comuns) (cve.mitre.org) mostra que de 2001 a 2004, 47 por cento de todas as vulnerabilidades de segurança rastreadas pela CVE eram questões de confiança de CENTRO UNIVERSITÁRIO DE BRASÍLIA - UniCEUB Faculdade de Ciências Exatas e Tecnologia - FAET Curso de Bacharelado em Ciência da Computação Disciplina: Segurança e Auditoria de Sistemas Professor: Maurício Rocha Lyra entrada. As questões mais conhecidas são saturações de buffer, bugs aritméticos com inteiros, scripts entre sites e bugs de injeção de SQL. Estamos começando a ver novas variações sobre este tema, como vulnerabilidades de injeção de Xpath e de injeção de protocolo LDAP. Você pode remediar as questões de confiança de entrada seguindo algumas regras simples. Primeiro, não procure apenas coisas que você sabe que são ruins; isso supõe que você conheça todas as coisas ruins e preveja todas elas no futuro. Procurar coisas ruins não tem problema, desde que essa não seja sua única defesa. Uma estratégia melhor é restringir a entrada daquilo que você sabe que é bom. Para linguagens de alto nível como o C# e Perl, gosto de usar expressões regulares para conseguir isso. Em seguida, rejeite aquilo que você sabe que é ruim. Por exemplo, se alguém solicitar remotamente um arquivo através de seu código e o nome do arquivo incluir um caractere especial (do tipo : ou \), recuse a solicitação. E não diga ao invasor o porquê; diga apenas "arquivo não encontrado." Finalmente, e isso não funciona em todos os cenários, desinfete os dados. Por exemplo, no caso de um servidor da Web, você deveria codificar com HTML a saída que venha de uma entrada potencialmente não confiável. Hábito nº 3: Modelar as ameaças contra seu código Você tem modelos de ameaças, certo? Os modelos de ameaça permitem que você entenda os riscos potenciais para seu software e que se certifique de que tenha as atenuações apropriadas no lugar. Mas, os benefícios da modelagem de ameaças se estendem além do design seguro. Os modelos de ameaças também podem ajudar na qualidade do seu código. Os modelos de ameaças informam a você a origem dos dados. Os dados são remotos ou locais? Os dados são de usuários anônimos ou de usuários mais confiáveis (autenticados), talvez administradores? Com essas informações à mão, você pode determinar se suas defesas são apropriadas. Por exemplo, o código acessível a usuários anônimos e remotos deve ser muito seguro. Não estou dizendo que o código acessível apenas ao administrador local não deva ser seguro, estou dizendo que o código acessível remotamente, especialmente sendo executado por padrão, tem de ser "à prova de balas", e isso significa mais defesas, mais revisão e mais atenção a detalhes. Mais que isso, o modelo de ameaça pode informar a natureza dos dados que estão sendo protegidos. Dados comerciais de alto nível e informações identificáveis pessoalmente, por exemplo, devem ser muito bem protegidas. Suas defesas são apropriadas? Certifique-se de que seus modelos de ameaça sejam precisos e atualizados, depois identifique todos os pontos de entrada em seu código e classifique-os pela acessibilidade – remota versus local e usuários muito privilegiados versus pouco privilegiados (ou sem privilégios). O código mais acessível deve ser revisto o mais detalhadamente e o mais cedo possível. Finalmente, reveja todos os códigos dos caminhos de dados anônimos; em outras palavras, comece em cada ponto de entrada acessível anonimamente e trace os dados pelo caminho, verificando se os códigos estão corretos. Hábito nº 4: Ficar um passo à frente A paisagem de segurança se desenvolve constantemente. Parece que a cada semana há novas variações de questões de segurança. Isso significa que você deve se desenvolver e aprender novas ameaças e defesas ou sofrerá as conseqüências. Algumas estratégias simples para estar um passo à frente da curva inclui ler alguns bons livros sobre o assunto de segurança de software de vez em quando. Além disso, aprenda com seus erros antigos e, ainda melhor, com os erros dos outros. Você pode fazer isso lendo a bugtraq — vá até securityfocus.com e se inscreva para receber informações da bugtraq em sua caixa de entrada. Mas, confie em mim neste próximo conselho: crie uma regra de caixa de entrada para mover as correspondências para uma pasta especial, para poder manipular o volume. É significativo. Hábito nº 5: Difundir! A difusão é uma técnica de teste inventada para encontrar bugs de confiabilidade. Descobriu-se que um percentual de bugs de confiabilidade são vulnerabilidades de segurança esperando pela exploração certa! Claro, uma saturação do buffer pode interromper um aplicativo, mas, dada uma transferência maliciosa bem feita, a interrupção pode não acontecer e o invasor poderia executar o código para fazer sua aposta. Nosso lema por aqui é "negar trabalho hoje é executar código amanhã." Quase toda a vulnerabilidade/bug para análise de arquivos foi encontrada por sorte ou difusão. A Microsoft encontrou vulnerabilidades de segurança analisando vários formatos de arquivos incluindo arquivos XLS, PPT, DOC e BMP. Muitos fornecedores tinham tido vulnerabilidades semelhantes, pois a análise de estruturas de dados complexas é uma tarefa difícil. O código complexo tem bugs, e alguns desses bugs revelarão vulnerabilidades de segurança. Você precisa difundir todos os códigos que analisam arquivos e tráfego de rede. O SDL (ciclo de vida do desenvolvimento da segurança) da Microsoft é muito específico no que se refere ao significado disso para os formatos de arquivo. Você precisa difundir todas as análises com 100 mil iterações de arquivos malformados usando um difusor de arquivo. Existem vários difusores razoáveis disponíveis e nós incluímos um difusor de arquivo, bem como código-fonte C++, no livro The Security Development Lifecycle, (em inglês) do qual fui co-autor junto com Steve Lipner (microsoft.com/MSPress/books/8753.asp). Uma última observação sobre difusão. Se ocorrer uma interrupção, não pense que é simplesmente uma CENTRO UNIVERSITÁRIO DE BRASÍLIA - UniCEUB Faculdade de Ciências Exatas e Tecnologia - FAET Curso de Bacharelado em Ciência da Computação Disciplina: Segurança e Auditoria de Sistemas Professor: Maurício Rocha Lyra interrupção. É provável que um grande percentual das supostas interrupções esteja implorando para que alguém escreva uma exploração. Portanto, não considere uma interrupção como "apenas uma interrupção." Hábito nº 6: Não escrever código inseguro Na Microsoft, utilizamos o conceito de Quality Gates (sistema de garantia de qualidade) para ajudar na redução das chances de o desenvolvedor verificar código vulnerável no produto. Os gates (portões) executam uma bateria de ferramentas de análise de código-fonte no código antes do check-in sinalizar algum problema. E quaisquer problemas identificados devem ser resolvidos antes do check-in ser concluído. Você também pode forçar regras estritas de código, como excluir o uso de funcionalidades banidas, como nenhuma chamada para strcpy nem strncat e nenhuma criptografia pobre. (A Microsoft baniu mais de 100 funções de tempo de execução C para código novo!). Por exemplo, em relação à criptografia, não permitimos DES (o tamanho da chave é muito pequeno), MD4, nem MD5 (estão ambos quebrados agora) em código novo, a menos que um padrão da indústria exija seu uso. Não reinvente funcionalidade. Se você tiver um código que analise um formato de arquivo específico, não precisa de duas ou três etapas da análise de código. Fique com aquela definida, torne-a robusta e a envolva em uma forma que possa ser utilizada em vários projetos. Finalmente, lembre-se de que as ferramentas não devem substituir o conhecimento de como escrever código seguro. É por isso que o aprendizado de segurança e privacidade é tão importante. Você precisa ter um conhecimento sólido dos conceitos para poder discernir as chamadas e os insights que suas ferramentas são capazes de fazer. Hábito nº 7: Reconhecer a assimetria estratégica Esse é um dos meus favoritos. Lembre-se de que como desenvolvedor de software, as probabilidades de segurança estão todas contra você. Gosto de chamar isso de "vantagem do invasor e dilema do defensor." Você precisa que o código seja 100% projetado corretamente, 100% do tempo, e isso é impossível. Para dificultar ainda mais, você precisa atingir essa meta insuperável com um orçamento fixo, enquanto tem de considerar requisitos de suportabilidade, compatibilidade, acessibilidade e outras "-bilidades". Um invasor pode gastar todo o tempo que quiser para encontrar um bug e depois contar ao mundo inteiro que seu aplicativo é inseguro. No Hábito nº 6, mencionei que você deveria parar de escrever código novo inseguro. Para o Hábito nº 7, você deve se concentrar em todos os códigos, pois os invasores atacam todos os códigos, independentemente da idade. Dedique algum tempo revisando código antigo, procurando vulnerabilidades de segurança, e considere seriamente a substituição de funcionalidades antigas e inseguras. Se você utiliza métodos de desenvolvimento ágeis, deve pensar em dedicar uma ou mais oportunidades para resolver códigos antigos, levando a qualidade dos códigos recentes até eles. Hábito nº 8: Utilizar as melhores ferramentas que puder Finalmente, utilize as melhores ferramentas possíveis. Eu adoro as ferramentas de análise de código-fonte e adoro qualquer tecnologia que me ajude a escrever código mais seguro. Conforme mencionei, as ferramentas não são panacéias, mas ajudam. E muito! As ferramentas também ajudam a escalar o problema de análise de código-fonte. As ferramentas conseguem examinar enormes quantidades de código rapidamente, com muito mais velocidade do que um humano seria capaz. E essa ajuda dá a você uma idéia de como alguns códigos podem ser "maus". Um dos meus truques favoritos é compilar o código usando o mais alto nível possível de avisos, como por exemplo, /W4 quando uso o Visual C++® ou –Wall quando uso gcc. Se você vir um grande número de avisos em um código, talvez ele tenha outros bugs que não foram localizados pelo compilador ou por outras ferramentas. Esse código deve estar sujeito a um maior grau de escrutinação antes de sua expedição (veja Hábito nº 3). Esses são os oito bons hábitos que percebi utilizados pelos desenvolvedores que respeito profundamente, tanto dentro quanto fora da Microsoft. Os hábitos por si só não o tornarão um desenvolvedor seguro e célebre, mas com certeza ajudarão! Michael Howard é gerente de programa de segurança sênior na Microsoft e se concentra no aprimoramento e processo seguro e nas práticas recomendadas. Ele é o co-autor de cinco livros de segurança, incluindo The Security Development Lifecycle, Writing Secure Code e 19 Deadly Sins of Software Security (em inglês).