Coluna do Kurt COLUNA Programação segura Nosso especialista de segurança fala sobre os softwares para auditar e garantir a segurança de seus programas. U m dos temas consistentes no livro Coders at Work, de Peter Seibel, é: Como ser um programador melhor? Essa ideia me é muito interessante porque, no mundo da segurança, percebo as pessoas cometendo sempre os mesmos erros. Depois de certo tempo, é de se esperar que as pessoas aprendessem a criar um arquivo temporário com segurança. Que ao menos aprendessem a criar, acessar e apagar buffers de memória de forma segura; no entanto, não é o caso de muitos programadores (incluindo alguns muito bons). Portanto, a resposta é prestar atenção às pessoas que escrevem códigos seguros, copiá-las e aprender com elas. Mas como fazer isso, principalmente quando não se tem tempo nem verba? Muitos programadores gostam de compartilhar, e a beleza do conhecimento e da informação é que podemos transmitir o que sabemos sem perder nada. Felizmente, alguns programadores se deram ao trabalho de organizar seu conhecimento e sabedoria em pacotes de softwares; de fato, o número de bibliotecas disponíveis hoje é impressionante (graças a essas pessoas, poucos hoje precisarão escrever um cliente HTTP ou um parser HTML). Portanto, qual o programa necessário para desenvolver softwares melhores e mais seguros? Valgrind O Valgrind é provavelmente a ferramenta de código aberto (licença GPL) mais madura para análise de binários e de código-fonte, não apenas com relação à segurança, mas também ao desempenho (os dois costumam caminhar juntos). Provavelmente o componente mais eficaz para detectar e solucionar problemas de segurança é o memcheck. O módulo memcheck encontra posições de memória que estejam sendo acessadas incorretamente, valores não inicializados que estejam sendo usados de modo perigoso, vazamentos de memória (sempre um potencial problema 16 de negação de serviço) e uma liberação ruim de blocos do heap (frees duplos ou desencontrados); além disso, ele pode detectar quando o programa informa blocos de memória sobrepostos para origem e destino. Instalação A instalação é trivial: yum install valgrind ou apt-get install valgrind A compilação do código-fonte também é fácil: $ $ $ $ $ # cd valgrind ./autogen.sh ./configure --prefix=/path make su make install Uso No uso do Valgrind, as coisas se complicam um pouco. O programa gera uma saída volumosa. Algumas são falsos positivos (ou fatos não tão perigosos) e separar o joio do trigo pode ser bem trabalhoso. Isso nos remete diretamente ao Debian Bug Report Log 363516. De forma resumida, nesse caso famoso o Valgrind avisava sobre o uso de memória não inicializada e, em vez de usar um arquivo de supressão (para fazer o Valgrind parar de reclamar) ou simplesmente examinar atentamente o código, o desenvolvedor optou simplesmente por comentar as linhas culpadas. Infelizmente, isso resultou em um pool de entropia previsível que acabou resultando em chaves OpenSSL totalmente previsíveis no Debian. Como acontece com várias ferramentas poderosas, se elas forem usadas http://www.linuxmagazine.com.br Insegurança | COLUNA sem a total compreensão, as consequências podem ser negativas. Dois excelentes vídeos no SecurityTube mostram os princípios do uso do Valgrind no Linux. Se você, assim como eu, precisa programar, mas não confia muito nas suas habilidades com relação à segurança e nem conhece muito bem programas como o Valgrind, o que você pode fazer? Linguagem mais segura Há uma solução realmente simples que dá conta da maioria dos problemas relacionados ao gerenciamento de memória, tais como alocação correta (uso de memória não inicializada etc.), uso seguro (estouro e underflow de buffer etc.) e garantia de que a memória seja destruída corretamente (free duplo, vazamentos de memória etc.): use uma linguagem de programação com gerenciamento de memória embutido (Python, Java, Perl etc.). Com elas, não é necessário se preocupar com o comprimento de uma string ou de um vetor ao criá-los. Simplesmente, crie uma string ou vetor e jogue os dados neles. O buffer se expande conforme necessário e, ao lê-lo, não será possível ir além do fim da string, pois o interpretador do programa simplesmente retornará um erro informando que não há mais dados ou itens para leitura (em vez de ler áreas aleatórias da memória). A desvantagem, logicamente, é que alguns problemas e programas não se adaptam bem a essas linguagens (quase todas as implementações dessas linguagens são em C, que é o pesadelo do gerenciamento de memória). Auditoria de código: PyChecker Além disso, por ser um fiel usuário do Unix, uso um programa simples e eficiente para verificação de código Python, o PyChecker. Trata-se de uma ferramenta para conferir código-fonte em Python. Embora o interpretador Python seja capaz de detectar vários erros de programação (e então levantar uma exceção, imprimir um erro e terminar o programa), alguns deslizes acabam escapando. O uso de uma variável global em uma classe, em vez de uma variável self (que existe somente naquela instância da classe e é, portanto, muito mais segura em um ambiente com threads), pode gerar vários tipos de problemas de difícil solução. No entanto, o PyChecker consegue detectá-los e passar a informação imediatamente. Instalar o PyChecker é fácil: $ $ $ $ wget http://download.site/pychecker-0.8.18.tar.gz tar -xf pychecker-0.8.18.tar.gz cd pychecker-0.8.18 python setup.py install Aprenda programação segura Mesmo com tudo isso, ainda não chegamos à questão da educação. Para uma programação segura é necessário entender, ou pelo menos conhecer, os vários tipos de falhas que comprometem essa questão. Desde as questões mais simples, como os vários tipos de estouros de buffer, até o esotérico “Use of a Non-reentrant Function in an Unsynchronized Context”. O projeto Common Weakness Enumeration (CWE – Enumeração das Falhas Comuns) foi desenvolvido exatamente para isso. Ele organizou uma lista completa das falhas de segurança dos softwares, com descrição, informações sobre as soluções e exemplos das falhas. Algumas das entradas do CWE possuem exemplos de códigos e, infelizmente, a maioria das entradas necessita de mais informações sobre a solução ou prevenção do problema. Mas, como já foi dito por alguém, “Agora você sabe, e saber já é meio caminho andado”. O próximo passo, logicamente, é aprender o comportamento e o mecanismo da programação segura. Apesar das dezenas de livros disponíveis atualmente, alguns muito bons, prefiro os recursos online. Um dos melhores e mais abrangentes documentos é o Secure Programming for Linux and Unix HOWTO de David A. Wheeler. Outro projeto que tenta melhorar a segurança dos softwares é o Open Web Application Security Project (OWASP – Projeto Aberto de Segurança de Aplicativos Web). Apesar de se dedicar a aplicativos web, a maioria do conteúdo dos guias de desenvolvimento, de revisão de código e de teste pode ser aplicado a softwares não baseados na web. O mais importante é que eles incluem ferramentas de verdade e documentação específica para a programação segura, além do “WebGoat”, um aplicativo propositalmente inseguro que ensina através de erros alheios. Conclusão Programação segura não é complicada. Ela basicamente requer disciplina, o que significa enfrentar as dificuldades, compreender o que as mudanças no código irão gerar (principalmente se o código original não for seu). n $ easy_install PyChecker Caso isso não funcione, é possível instalá-lo manualmente baixando o tarball do PyChecker, descompactando-o e executando a instalação manualmente: Linux Magazine #63 | Fevereiro de 2010 Kurt Seifried é consultor de segurança da informação especializado em redes e Linux desde 1996. Ele frequentemente se pergunta como a tecnologia funciona em grande escala mas costuma falhar em pequena escala. 17