Segurança de Aplicação

Segurança em seu Pipeline de CI/CD

Hoje vamos abordar a segurança no seu pipeline CI/CD. E para melhor introduzir este tema, precisamos dar atenção a alguns conceitos.

No cenário atual, o mercado espera que os softwares tenham uma velocidade de entrega cada vez maior. Para que isso seja possível, os desenvolvedores buscam cada vez mais se adequar à práticas como CI/CD –  cujo cenário vamos detalhar a seguir.

  1. O primeiro conceito se refere ao Continuous Integration (CI), uma tentativa das equipes em criar uma estrutura que permita a realização de criação e testes de software de forma automatizada.
  1. O segundo conceito que temos que ter claro é o Continuous Delivery (CD), onde buscamos por um processo automatizado de construção, configuração e a criação de um pacote onde o código será entregue.

Estes dois conceitos precisam que práticas e ferramentas sejam usadas em conjunto para entregar às equipes de desenvolvimento as vantagens e agilidade que exigem hoje os processos de desenvolvimento.

Por isso, neste artigo, vamos falar um pouco sobre a implementação de segurança nesse pipeline e buscar mostrar que o uso correto e seguro de estruturas baseadas em CI/CD podem entregar bem mais que agilidade ao processo – elas podem entregar um código mais seguro.

Entendendo o pipeline e garantindo a segurança do pipeline

O pipeline de CI/CD constitui um dos pontos chave dentro de um processo de desenvolvimento. Como tal, deve ser protegido para que não possa ser comprometido. Desta forma, evita-se, por exemplo, que sejam distribuídos códigos alterados e/ou com partes de códigos maliciosos usando uma estrutura  confiável.

Quando percebemos que por esta estrutura passam nossos códigos, percebemos que garantir sua segurança é fundamental para que todo nosso processo seja confiável e entregue realmente o código que esperamos.

Mas atenção: este não é necessariamente um processo simples.

De forma geral, precisamos pensar em proteger nosso processo – ou pipeline – em três frentes distintas, mas que atuam em conjunto para entregar segurança ao pipeline.

Primeiro temos que pensar em proteger o pipeline em si.

Controle de acesso

Neste primeiro momento, para focarmos na proteção do pipeline em si, precisamos entender quais práticas básicas de segurança devem ser abordadas. 

Então, podemos começar por um maior controle sobre quem pode ou não ter acesso ao pipeline. 

Tendo um controle rigoroso sobre quem envia as alterações de nosso código para os repositórios que serão usados como base de armazenamento, podemos garantir a primeira camada de proteção de nosso pipeline.

Não podemos deixar de lado outros pontos. É o caso, por exemplo, da segurança da conexão dos nossos desenvolvedores, ou mesmo a segurança do equipamento utilizado pela equipe. Tudo precisa ser avaliado e olhado como um ponto de possível comprometimento.

Aqui também cabe o uso de uma metodologia usada muito para design de código, mas que pode ser usado em outros momentos. A utilização de conceitos de Modelagem de Ameaças para verificar quais pontos podem ser mais frágeis dentro do processo é também uma boa alternativa.

Olhando agora de um outro prisma, precisamos garantir a segurança do que trafega por este pipeline.

Neste ponto, precisamos garantir que o que é enviado, armazenado e movimentado dentro do pipeline de CI/CD seja seguro e seja constantemente revisado.

Este objetivo é alcançado quando temos em nosso processo controles que garantem que o código será validado por meio de ferramentas de análise estática, e também por meio de code review – estes, de preferência, feitos de forma manual. Assim, as possíveis vulnerabilidades encontradas podem ser corrigidas.

Segurança por meio da automação

O terceiro e último ponto é garantir que haja uma segurança baseada em um processo automatizado. Afinal, isso pode garantir que a segurança do processo ocorra com a menor intervenção possível.

Garantir a segurança por meio da automação é também uma tentativa de se tirar da equação o fator humano. Afinal, sabemos que, muitas vezes, esta pode vir a ser a fonte de uma falha.

Vamos tentar entender cada um destes pontos de forma mais detalhada.

Vamos começar esta parte do texto explicando um pouco sobre o vem a ser um pipeline.

De forma geral, um pipeline é um processo estruturado e que responde a um conjunto de ações e tarefas. Para nós, um pipeline é um processo estruturado que garante que um software inicialmente criado na máquina de um desenvolvedor possa ser entregue a sua produção de forma automatizada.

Usando conceitos simples, podemos representar na imagem abaixo o que vem a ser um pipeline.

De forma geral, podemos entender que o código inicialmente está sendo escrito na máquina do desenvolvedor, finalizado a sua codificação o código é então enviado para um repositório de códigos.

Chegando ao repositório de código, temos o primeiro conceito, que é o de Continuous Integration. Um conjunto de ferramentas e ações que são realizadas no código para garantir a primeira camada de proteção, realizando os primeiros testes de código.

Este código é então enviado para uma segunda etapa, o Continuous Delivery, responsável por “empacotar” o software para que este, por meio de outras ferramentas, seja então colocado para produção.

Claro que temos mais coisas neste processo, o intuito aqui é apenas dar uma visão de um processo de CI/CD e como um pipeline funciona.

Se já entendemos isso, podemos seguir e entender como conseguimos mais segurança para nosso pipeline.

Security by Design

Quando pensamos em segurança de software, temos uma fonte que sempre buscamos consultar, neste caso o OWASP tem um conceito de secure by design, e em seu documento estão listados 10 princípios que devem ser seguidos para entregar um código seguro.

No nosso caso, podemos tomar emprestados estes conceitos e pensar neles como sendo os pontos de partida para tornarmos nosso pipeline mais seguro. Claro que não conseguiremos usar todos, mas podemos tomar alguns destes conceitos para nosso propósito.

Abaixo estão listados os 4 conceitos que vamos tomar emprestados e, no decorrer do artigo, vamos colocar cada um deles para tornar nosso pipeline mais seguro.

Ilustrando

Imagine um pipeline de desenvolvimento como este da imagem abaixo:

Em uma configuração padrão, podemos ver que há inúmeros pontos onde o usuário possui um permissionamento não correto para o processo que queremos proteger. Desta forma, precisamos entender qual o melhor fluxo e como ele pode ser avaliada para entregar a nosso processo o conceito de menor privilégio.

Se pensarmos em reduzir a superfície de ataque e olharmos a imagem de nosso pipeline, podemos perceber que não há razão direta para que o desenvolvedor tenha acesso de escrita dentro do repositório de imagens ou mesmo do cluster. Neste exemplo trabalhamos com containers como resultado final.

Assim, com esta simples mudança, podemos garantir também que todos que terão acesso a este pipeline estarão  com uma separação de funções bem definidas (separation of duties). Afinal, isso nos garante um pouco mais de segurança e o que nos entregaria um processo mais como o da imagem abaixo.

Mesmo nosso processo estando bem melhor agora, ainda temos que imaginar que nosso pipeline ainda é um grande alvo, pois nele temos acesso ao código, credenciais e outras informações que são de grande interesse a atacantes.

Nesta segunda imagem podemos ver que houve uma redução considerável no permissionamento. Então, criamos agora, por meio das linhas pontilhadas vermelhas, as fronteiras lógicas entre as separações de funções.

Próximo passo: implementar melhorias

Precisamos ainda pensar em como melhorar a segurança desse pipeline.

Neste ponto, podemos adotar como ponto de avaliação o uso de um conceito chamado defesa em profundidade, onde vamos criar camadas de segurança que tentam limitar ainda mais o acesso ou mesmo o uso de determinado ativo.

No caso de nosso pipeline, podemos imaginar situações onde o processo de autenticação seja realizado por meio de um mecanismos de segundo fator de autenticação (2fA), onde mesmo tendo sua senha sido digitada, o desenvolvedor deve introduzir um segundo fator, talvez um token, para que o sistema valide seu acesso.

Com estas simples ações, podemos manter e aumentar a segurança do pipeline de desenvolvimento.

Claro, existem outro conceitos e outras formas que podemos usar para manter e garantir a segurança do pipeline, este é apenas um exemplo de como podemos ir buscar em outras áreas de segurança os conceitos necessários para garantir a segurança de um processo.

Garantindo a segurança no Pipeline

A segurança no pipeline é um processo de implementação de boas regras de segurança, mas vamos tentar tratar cada um destas práticas que podemos usar para a segurança do pipeline.

Abaixo vamos colocar os pontos de segurança de acordo com o que é mais usualmente encontrado em um processo de CI/CD, mas podem ter algumas variações de acordo com o seu processo.

O Desenvolvedor

Em um primeiro momento, vamos tentar garantir a segurança no extremo inicial do processo. Nosso ponto de avaliação será o desenvolvedor. Lembre-se que aqui não vamos falar de como o desenvolvedor deve  logar no sistema ou na máquina –  esse assunto já foi tratado anteriormente.

Neste ponto, queremos já começar a tratar da segurança do software, e o desenvolvedor deve ser o primeiro ponto de contato. Para isso, vamos imaginar que a segurança já deve ser pensada diretamente na ferramenta de trabalho. Com isso, podemos imaginar na possibilidade em usar plugins de IDE para que este ao escrever o código já vá passando por um processo de validação.

As IDEs mais atuais e as ferramentas de verificação já possuem em sua grande maioria a possibilidade de acoplar um plugin que possa auxiliar o desenvolvedor a já ter alguns alertas sobre possíveis falhas ou melhores práticas de codificação. Isso já retira da equação muito da subjetividade de entendimento em segurança do desenvolvedor.

Mesmo que alguns desenvolvedores não vejam como necessários, o uso destes plugins podem ajudar a ser a primeira linha de defesa do software, bem como ajudar a ganhar tempo em um processo que já exigido a ser muito ágil.

Revisão dos códigos

Manter a segurança dos códigos é um processo que deve ser abraçado por todos, e isso inclui, por exemplo, e dentro do possível, que haja a revisão dos códigos desenvolvidos em pares. Afinal, isso aumenta a possibilidade de encontrar vulnerabilidades, bem como também aumenta o conhecimento do desenvolvedor em princípios de segurança de código. Uma boa fonte de pesquisa pode ser encontrado em um conjunto de documentos da OWASP chamado “Cheat Sheet Series”. Este conjunto de documentos pode ajudar a identificar boas práticas em codificação e assim melhorar a segurança do código.

Um bom exemplo do que podemos esperar neste conjunto de documentos é o que está descrito no documento que fala sobre autenticação, vale a leitura para perceber que há muito o que ser avaliado em um simples processo.

Testes estáticos e dinâmicos de código

Dentro de um processo de CI/CD, precisamos garantir que a segurança do código está sendo pensada em cada minuto. Um dos pontos mais debatidos em momentos de utilização de testes é sobre escalabilidade.

Neste momento, podemos dizer que o uso de ferramentas de testes estáticos e dinâmicos entregam ao processo uma agilidade que é necessária hoje. 

No entanto, precisamos entender que estas ferramentas deverão passar por uma validação, garantindo que seus resultados sejam os mais confiáveis possíveis. Entendemos a importância das ferramentas, mas não acreditamos que elas sejam as “balas de prata” da segurança de códigos.

As ferramentas devem ser encaradas como um suporte ao processo de revisão manual de código, este com muito mais probabilidade de encontrar resultados mais precisos. Isso não quer dizer que as ferramentas não sejam precisas ou mesmo ineficientes, mas sutilezas em algumas falhas somente serão percebidos por um profissional experiente e que tenha seu foco em avaliar todos os ângulos de um possível erro.

Testes unitários e funcionais

Da forma como realizamos nossos testes hoje não entregamos ao processo muito coisa, precisamos melhorar a forma como realizamos nossos testes.

Para melhorar, os responsáveis pelos testes deveriam buscar avaliar se seus scripts de testes estão preparados para testar pelo menos as vulnerabilidades mais comuns. Uma boa base, neste caso, é o OWASP TOP TEN 2017.

Da mesma forma que testes estáticos e dinâmicos, os testes unitários precisam ser elaborados para serem executados rapidamente, deixando os testes mais longos para serem executados em momentos previamente planejados e de forma regular.

Há hoje diversas ferramentas que podem realizar estas tarefas, cabendo aos responsáveis a configuração e uso correto.

Não podemos esquecer que ainda há os testes funcionais. Eles tradicionalmente são realizados para identificar e validar o que as aplicações devem fazer – e assim são escritos – buscando entender se o que está requisitado é o que o software faz.

Requisitos negativos do software

Mas atenção: precisamos entender que há coisas que o software não deve fazer, e testar isso também. Estes são chamados de requisitos negativos do software.

Assim, quando há a criação de casos de uso dos usuários, devemos montar os casos inversos dos usuários – resultados que não devem aparecer como resultado de uma ação do software. Um possível exemplo pode ser visto neste documento da OWASP.

Este testes podem ser automatizados por uma série de ferramentas. Estas, por sua vez, podem ser integradas ao processo de nosso CI/CD, o que garante agilidade e automatização do processo.

O teste tradicional enfatiza o que um programa deve fazer e os casos de teste funcional geralmente são escritos em um formato semelhante (requisitos positivos). No entanto, há uma importância muito maior para requisitos negativos nos testes de segurança, descrevendo o que um sistema não deve permitir. O conceito ágil de histórias de usuário, descrevendo o que os usuários podem fazer, tem um conceito inverso na forma de histórias de usuários ruins que são úteis para a formulação de casos de teste de segurança funcional.

Desta forma, podemos entender que um processo de desenvolvimento estaria com a sua estrutura mais preparada para garantir a segurança do seu software. No entanto, isso não é um documento estático e finito em si mesmo. É necessário que seja avaliado e entendido como um ponto de partida, e não como o mapa final da segurança do processo.

Concluindo

Sabemos que a integração da segurança nos processos automatizados do DevOps facilita e agiliza todo o processo de desenvolvimento. Então, por consequência, garante a entrega de software seguro. 

Percebemos que o modelo de DevOps está evoluindo constantemente e rapidamente e sempre há novos produtos e ferramentas chegando muito rapidamente. 

Com este artigo, esperamos ter dado início a algumas idéias sobre como melhorar a segurança de seu pipeline. No entanto, lembramos que os profissionais da Conviso estão à disposição para mostrar como estes e outros conceitos de segurança de aplicações podem transformar o dia a dia da sua empresa.

Nova call to action
About author

Articles

Mais de 15 anos de experiência em segurança da informação e aplicações, graduado em processamento de dados, trabalhei como professor universitário e participei ativamente como instrutor de treinamento para mais de 6000 desenvolvedores em equipes de TI. Sou pai de duas meninas e trader nas horas vagas.
Related posts
Code FightersSegurança de Aplicação

Como integrar o Semgrep no CI/CD e enviar os resultados para a Conviso Platform

Atualmente, é uma prática muito comum integrar verificações de segurança durante a fase de…
Read more
Segurança de Aplicação

Impactos Negativos Gerados pela Ausência de Logs e Monitoramento de Segurança

Primeiramente, os logs são registros de atividades gerados também por sistemas, aplicações e…
Read more
Segurança de Aplicação

Dockers e Containers

Containers são soluções cada vez mais populares na indústria de desenvolvimento de software.
Read more

Deixe um comentário