Encontrando dependências com falhas de segurança em aplicações Java
Utilizar bibliotecas de terceiros pode aumentar consideravelmente a produtividade da equipe e permitir desenvolver softwares melhores, mas isso sempre introduz um risco. Será que alguma dependência do seu projeto contém falhas de segurança conhecidas? Este é um dos fatores que temos que nos preocupar continuamente durante o desenvolvimento e manutenção dos nossos sistemas.
OWASP
A Open Web Application Security Project (OWASP) é uma organização sem fins lucrativos cuja missão é tornar a segurança de software visível, para que indivíduos e organizações sejam capazes de tomar decisões informadas. Trata-se de um grupo com mais de 45.000 participantes ao redor do mundo que disponibiliza material de qualidade sobre segurança de software de forma neutra.
Em seu site oficial é possível encontrar desde informações sobre tecnologias específicas como, por exemplo, boas práticas de uso do Hibernate, até recomendações com exemplos das vulnerabilidades mais comuns.
Além de muito conteúdo na forma de wiki, a OWASP disponibiliza também algumas ferramentas como a OWASP Dependency Check, cujo objetivo é justamente automatizar a busca por dependências que contenham algum tipo de falha de segurança conhecida. Atualmente ela tem suporte oficial para Java e .NET e suporte experimental para Ruby, Node.js, Python e C/C++.
Utilizando a OWASP Dependency Check
Em aplicações Java, a OWASP Dependency Check pode ser utilizada de diversas formas: linha de comando, scripts ant, maven, gradle ou através de um plugin jenkins. Neste artigo abordo como realizar a verificação através da linha de comando e também como configurar projetos que utilizem gradle na geração de sua build. Mas caso ainda esteja utilizando o maven, não se preocupe, a configuração é bastante similar.
Para fazer a analise via linha de comando é necessário baixar e descompactar a dependency-check-cli, que pode ser encontrada neste página (na sessão quick download). Em seguida basta executar o dependency-check.sh ou dependency-check.bat (se estiver em um ambiente Windows) passando pelo menos os dois parâmetros obrigatórios, que são o nome do projeto e a pasta onde encontram-se as dependências. O comando completo será algo como:
dependency-check.sh --project “Minha aplicação” --scan build
Onde "Minha aplicação" seria o nome do projeto e build a pasta onde encontram-se as dependências a serem analisadas.
A primeira execução pode demorar vários minutos, pois a OWASP dependency check irá baixar toda a base de informações da national vulnerability database (NVD). Mas não se preocupe, a segunda vez será muito mais rápida, já que apenas novas atualizações desta base serão transferidas.
Ao final da execução será criado o arquivo dependency-check-report.html. Para consultar o resultado da analise basta abri-lo em um navegador. Caso tenha sido encontrada alguma vulnerabilidade, o relatório irá apontar qual a dependência com problema, uma pequena descrição da falha e alguns links úteis para que possa obter mais informações.
Integrando ao gradle
Executar via linha de comando pode ser útil em alguns casos mas o ideal é integrar este tipo de analise na geração da build do projeto. Desta forma fica fácil adicionar um novo passo na pipeline, automatizando assim todo o processo.
Ao realizar esta validação na geração da build ganhamos também a possibilidade de rejeita-la caso seja encontrado algum problema, evitando que uma potencial falha de segurança alcance o ambiente de produção.
Para que seja possível a verificação através do gradle, precisamos incluir o plugin org.owasp:dependency-check-gradle, conforme exemplo abaixo:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.owasp:dependency-check-gradle:3.3.4'
}
}apply plugin: 'org.owasp.dependencycheck'
Nesta configuração utilizamos a versão 3.3.4 do plugin mas você pode consultar qual a última versão estável nesta página.
Em seguida basta executar a tarefa dependencyCheckAnalyze através do gradle:
./gradlew dependencyCheckAnalyze
Um resumo do resultado será impresso no console e um relatório completo é disponibilizado no arquivo build/reports/dependency-check-report.html.
Este plugin possui diversas propriedades com valores que podem ser alterados. Uma delas é a failBuildOnCVSS. Cada falha possui uma nível de severidade, que pode variar entre zero e dez. Quanto maior a nota, mais grave é a falha. A propriedade failBuildOnCVSS indica qual nível de severidade que permitimos que seja encontrada sem que nossa build seja interrompida. Seu valor padrão é 11, o que faz com nossa build nunca falhe. Considerando que caso encontre uma dependência com problemas de segurança durante a construção de nosso projeto devemos considera-lo como sendo inapto, precisamos alterar este valor. Para fazer isso, basta adicionar a seguinte configuração:
dependencyCheck {
failBuildOnCVSS = 0
}
Desta forma, nossa build falhará caso seja encontrado algum problema de segurança. Outras propriedades podem ser definidas da mesma forma. Consulte nesta página os valores padrões das propriedades deste plugin.
Quando executar?
Não basta configurar o plugin, precisamos executa-lo frequentemente, afinal todos os dias novas falhas de segurança são reportadas. Minha abordagem para este problema é a mais simples possível: executo o plugin todas as vezes que faço um push em meu repositório git.
Sempre utilizo algum sistema de integração contínua automatizada (como o gitlab-ci ou jenkins), e costumo adicionar a execução do OWASP Dependency Check como um dos últimos passos da pipeline. Desta forma, toda vez que é feito um push no repositório, em algum momento (normalmente após todos os testes automatizados) é executada a validação das dependências. Esta abordagem, embora simples de aplicar, possui alguns problemas.
O primeiro é que adiciona um tempo a mais na geração da build. Mas isso pode ser minimizado ao configurar corretamente nossa integração contínua para que a pasta onde são armazenados os dados da NVD seja reaproveitada (lembre-se que a primeira execução é bastante demorada, portando é importante efetuar um cache deste diretório). O segundo grande problema é que a validação ocorre apenas quando o projeto sofre alguma alteração e uma nova build é gerada. Esta pode ser uma abordagem ruim em projetos que não estejam sofrendo alterações, pois podem ter sido gerados com uma falha de segurança que ainda não havia sido descoberta na época mas que algum tempo depois foi divulgada. Neste tipo de cenário provavelmente seria mais interessante efetuar esta validação em um intervalo de tempo fixo (todos os dias, por exemplo).
Futuro
Com a evolução de plataformas como a github, novas técnicas ainda mais simples estão aparecendo. Hoje, ao versionar seu código JavaScript ou Ruby no github (em repositórios públicos ou mesmo privados), já é possível habilitar alertas de segurança para que o próprio github já faça este tipo de verificação automaticamente e continuamente para você.
A tendência é que este tipo de verificação fique mais fácil e transparente para os times de desenvolvimento.
Conclusão
A OWASP Dependency Check permite encontrar dependências que possuem algum tipo de falha de segurança conhecida. Embora a primeira execução seja demorada, o processo é muito simples e fácil de introduzir em nossos projetos.
A configuração descrita neste artigo nos ajuda a evitar que nossos sistemas sejam atacados e informações confidenciais sejam vazadas ou manipuladas indevidamente, aumentando assim a segurança de nossos sistemas.