Segurança de Integridade de Software
LidoEsta aula aborda a segurança de integridade de software, focando em assinatura digital, verificação, pipelines de build e atestação. O objetivo é garantir que o software não foi adulterado desde sua criação até a implantação, usando práticas como assinatura de artefatos, verificação contínua e atestação de pipeline.
A segurança de integridade de software é um pilar fundamental para garantir que o código e os artefatos que você produz não foram modificados de forma maliciosa ou acidental durante o ciclo de vida do desenvolvimento. Em um mundo onde ataques à cadeia de suprimentos de software estão se tornando cada vez mais comuns, é essencial adotar mecanismos que assegurem que o software que você entrega é exatamente o que foi construído e aprovado. Nesta aula, vamos explorar os conceitos de assinatura digital, verificação, pipelines de build seguros e atestação, que juntos formam uma camada robusta de proteção contra adulteração e comprometimento.
Imagine que você baixa um aplicativo de um site oficial. Como saber se o arquivo que você recebeu é genuíno e não foi substituído por um malware? A resposta está na assinatura digital e na verificação. Da mesma forma, em pipelines de CI/CD, cada etapa do build deve ser verificada e atestada para garantir que o artefato final é íntegro. Vamos mergulhar em cada um desses tópicos, com exemplos práticos e boas práticas para implementar em seus projetos.
Signing
Assinatura digital (signing) é o processo de aplicar uma chave criptográfica a um artefato (como um binário, um pacote, ou até mesmo um commit) para garantir sua autenticidade e integridade. A assinatura é feita com uma chave privada, e a verificação é feita com a chave pública correspondente. Se qualquer parte do artefato for alterada após a assinatura, a verificação falhará, indicando que o arquivo foi adulterado.
Existem várias ferramentas para assinar artefatos, como GPG (GNU Privacy Guard) para assinar commits e tags, e ferramentas específicas como `cosign` para assinar imagens de contêiner. A assinatura pode ser aplicada em diferentes níveis: no código-fonte, nos artefatos compilados, ou até mesmo nos metadados. No contexto de integridade de software, o importante é que a assinatura seja feita por uma entidade confiável (como um mantenedor ou um sistema de build) e que a chave privada seja protegida adequadamente.
Exemplo de assinatura de um arquivo com GPG:
# Gerar uma chave GPG (se não tiver)
gpg --full-generate-key
# Assinar um arquivo
gpg --detach-sign --armor arquivo.txt
# Isso gera um arquivo .asc com a assinatura
Para assinar uma imagem de contêiner com cosign:
# Instalar cosign e configurar as credenciais
cosign sign --key cosign.key minha-imagem:latest
Boas práticas de assinatura incluem usar chaves separadas para diferentes propósitos, armazenar chaves privadas em hardware seguro (como YubiKey) ou em cofres de chaves, e nunca compartilhar chaves privadas. Além disso, é importante assinar todos os artefatos relevantes, não apenas o binário final, mas também as fontes e os manifestos.
Verification
Verificação é o processo de confirmar que a assinatura de um artefato é válida e que o conteúdo não foi alterado. Isso é feito usando a chave pública do assinante. A verificação pode ser feita manualmente (por exemplo, verificando um commit assinado) ou automaticamente em pipelines de deploy, onde cada artefato é verificado antes de ser implantado.
Ferramentas como `gpg --verify`, `cosign verify` e `slsa-verifier` são comuns. A verificação garante que o artefato veio de uma fonte confiável e que não foi modificado entre a assinatura e o consumo. Sem verificação, a assinatura é inútil, pois não há como saber se a chave pública usada é a correta ou se o artefato foi substituído.
Exemplo de verificação de um arquivo assinado com GPG:
# Verificar a assinatura
gpg --verify arquivo.txt.asc arquivo.txt
Exemplo de verificação de uma imagem de contêiner com cosign:
cosign verify --key cosign.pub minha-imagem:latest
Em pipelines, é crucial verificar a assinatura dos artefatos antes de usá-los. Isso pode ser feito em um estágio separado do pipeline, garantindo que apenas artefatos assinados e verificados sejam promovidos para produção. Além disso, a verificação deve ser feita com uma chave pública confiável, que deve ser gerenciada com cuidado para evitar que atacantes substituam a chave pública por uma maliciosa.
Build pipelines
Build pipelines são os processos automatizados que compilam, testam e empacotam o código-fonte em artefatos prontos para distribuição. A segurança desses pipelines é crítica, pois se um atacante conseguir comprometer o pipeline, ele pode injetar código malicioso em todos os artefatos produzidos. Portanto, é essencial implementar práticas de segurança em cada etapa do pipeline.
Algumas práticas recomendadas incluem: usar ambientes isolados e imutáveis para builds, garantir que as dependências sejam baixadas de fontes confiáveis e verificadas, usar segredos (como chaves de assinatura) armazenados em cofres (como HashiCorp Vault) e acessíveis apenas a tarefas autorizadas, e aplicar controle de acesso rigoroso ao pipeline. Além disso, é importante registrar logs de todas as atividades e monitorar o pipeline para detectar anomalias.
Um exemplo de pipeline de build seguro usando GitHub Actions:
name: Build and Sign
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: make build
- name: Sign artifact
run: |
echo "${{ secrets.SIGNING_KEY }}" | gpg --import
gpg --detach-sign --armor dist/meu-app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: meu-app
path: dist/
Note que a chave de assinatura é armazenada como um segredo no GitHub Actions, e o pipeline importa a chave apenas durante o passo de assinatura. Isso reduz a exposição da chave. Além disso, o pipeline deve ser configurado para falhar se a assinatura falhar, evitando que artefatos não assinados sejam publicados.
Attestation
Atestação (attestation) é o processo de fornecer uma declaração verificável sobre a origem e as características de um artefato. Enquanto a assinatura garante a integridade e autenticidade do artefato em si, a atestação fornece informações adicionais sobre como o artefato foi construído, quais materiais foram usados, quem o construiu, etc. Isso é fundamental para a conformidade e para a confiança na cadeia de suprimentos de software.
O padrão SLSA (Supply-chain Levels for Software Artifacts) define níveis de atestação, que vão desde o nível 1 (documentação básica) até o nível 4 (atestações completas e verificáveis). Ferramentas como `in-toto` e `sigstore` (com `cosign` e `rekor`) são usadas para criar e verificar atestações. As atestações são frequentemente armazenadas em um log transparente (como o Rekor) para que possam ser auditadas publicamente.
Exemplo de atestação com cosign e Rekor:
# Criar uma atestação (por exemplo, um documento JSON descrevendo o build)
cosign attest --predicate build.json --key cosign.key minha-imagem:latest
# Verificar a atestação
cosign verify-attestation --key cosign.pub minha-imagem:latest
As atestações podem incluir informações como o repositório de origem, o commit hash, o nome do pipeline, as dependências usadas, e até mesmo a política de segurança aplicada. Isso permite que os consumidores do artefato tomem decisões informadas sobre se devem confiar nele. Por exemplo, um sistema de implantação pode exigir que a atestação indique que o build foi feito a partir de um repositório aprovado e com uma política de segurança específica.
Boas Práticas e Observações Finais
Para implementar uma estratégia robusta de integridade de software, considere as seguintes boas práticas:
- Assine tudo: Assine não apenas os binários finais, mas também as imagens de contêiner, os pacotes de dependências e os metadados.
- Verifique continuamente: Integre a verificação de assinatura em seus pipelines de CI/CD e em seus processos de implantação.
- Use atestações: Adote padrões como SLSA e ferramentas como sigstore para fornecer atestações verificáveis sobre seus artefatos.
- Proteja as chaves: Armazene chaves privadas em hardware seguro ou cofres de chaves, e use segredos em pipelines para evitar exposição.
- Monitore e audite: Registre logs de todas as operações de assinatura, verificação e atestação, e monitore-os para detectar atividades suspeitas.
Lembre-se de que a segurança de integridade é um processo contínuo, e você deve revisar e atualizar suas práticas regularmente para se manter à frente das ameaças.
Exercícios
- Explique a diferença entre assinatura digital e verificação. Dê um exemplo de cada.
- Por que é importante proteger as chaves privadas de assinatura? Cite duas práticas recomendadas.
- Descreva duas medidas de segurança que você pode implementar em um pipeline de build para garantir a integridade dos artefatos.
- O que é atestação e como ela difere da assinatura? Dê um exemplo de uso.
- Qual é o papel do padrão SLSA na segurança de integridade de software? Cite dois níveis e suas exigências.