Supply Chain Security: Assinatura com Sigstore e SBOM Real em CI/CD
Como a Basilisk implementa cosign, SLSA e CycloneDX em pipelines reais para mitigar ataques tipo SolarWinds, XZ Utils e dependency confusion.

Quando o backdoor do XZ Utils (CVE-2024-3094) quase entrou em distribuicoes Linux estaveis em marco de 2024, ficou claro que assinar tag no GitHub nao e mais suficiente. A cadeia de suprimentos de software virou alvo prioritario porque um unico maintainer comprometido propaga codigo malicioso para milhoes de hosts em horas. Na Basilisk OffSec trabalhamos os dois lados: simulamos ataques contra pipelines de clientes e ajudamos times a fechar as portas que encontramos. Este post consolida o que aplicamos em CI/CD real usando Sigstore cosign, SLSA nivel 3 e SBOMs CycloneDX, com foco em pragmatismo, nao em compliance teatral.
Por que a cadeia de suprimentos virou o alvo
Um atacante tem dois caminhos ate sua producao: a porta da frente (atacar seus servicos expostos) ou a porta dos fundos (comprometer algo em que voce ja confia). A porta dos fundos escala melhor. O incidente SolarWinds mostrou que um processo de build adulterado atinge 18.000 organizacoes de uma vez. O XZ mostrou que um atacante paciente ganha confianca social ao longo de dois anos, obtem direitos de maintainer e planta um backdoor que so dispara sob condicoes muito especificas.
O denominador comum: o elo mais fraco nunca e a criptografia, e a cadeia humana e de processo em volta do build. Por isso a resposta nao e uma unica tecnologia, mas uma cadeia de assinatura (quem construiu), proveniencia (como e onde foi construido) e inventario (o que tem dentro). Faltando um elo, voce voa as cegas. O incidente CodeCov de 2021, onde uma credencial vazada adulterou um script uploader em bash, foi o alarme para a camada de assinatura.
Cosign keyless: Fulcio e Rekor
O primeiro passo e abandonar a ideia de chave privada longa-data armazenada em secret manager. O cosign keyless usa OIDC do GitHub Actions, GitLab CI ou Buildkite para emitir um certificado efemero via Fulcio, valido por 10 minutos, e registra a assinatura no Rekor (log transparente append-only). Isso elimina a categoria inteira de vazamento de chave de assinatura. Um workflow tipico tem tres jobs: build reproducivel, sign com cosign sign --yes e attest com predicado SLSA Provenance v1.0.
O pulo do gato do log transparente: mesmo que um atacante obtenha brevemente uma identidade OIDC valida, toda assinatura fica registrada de forma publica e imutavel. Voce pode perguntar retroativamente quais artefatos foram assinados com qual identidade e em que momento, e cacar anomalias. Em projetos de cliente vimos reducao de 90% no tempo de rotacao de credenciais ao migrar de chaves PGP para keyless, e isso conversa diretamente com o que discutimos em AppSec Shift-Left: SAST, SCA e Secrets Scanning sem Travar o Time.
SBOM com Syft e CycloneDX
SBOM nao e XML que ninguem le. Geramos CycloneDX 1.6 com syft no momento do build, incluindo hashes SHA-256 de cada componente, licenca SPDX e fornecedor. Uma invocacao real: syft packages dir:. -o cyclonedx-json=sbom.json. O arquivo vai junto da imagem OCI como artefato referenciado, atado a imagem via cosign attest --predicate sbom.json --type cyclonedx, nao perdido em um S3 que ninguem vai achar em seis meses.
O valor de uma SBOM aparece no dia do proximo zero-day. Quando uma CVE critica surge em uma dependencia transitiva, a pergunta nao e abstrata, e operacional: quais das nossas 200 imagens em execucao contem a versao vulneravel? Sem SBOM isso e arqueologia de varios dias. Com um inventario SBOM consultavel e uma query de minutos. E exatamente por isso que voce ata a SBOM ao artefato e nao a uma pagina de wiki.
Scan com Grype e Policy-as-Code
Para validar, rodamos grype no pull request e tambem em background contra o registry, porque CVE nova aparece depois do merge. Uma imagem que estava limpa ontem pode carregar uma vulnerabilidade critica hoje sem uma unica linha de codigo mudar. Combinamos isso com policy-as-code via Kyverno ou Conftest, bloqueando deploy se a SBOM tiver dependencia com CVSS acima de 7.0 sem excecao documentada.
O mecanismo de excecao importa. Uma policy dura sem caminho de excecao documentado e contornada assim que bloqueia um deploy urgente, e ai ela morre. Usamos um arquivo VEX (Vulnerability Exploitability eXchange) para declarar que uma dada CVE nao e explotavel no contexto especifico, com justificativa e data de expiracao. Esse mesmo modelo de policy aparece quando discutimos Hardening de Linux Servidor: CIS Benchmark Aplicado sem Quebrar Producao em servidores de producao.
Entendendo os niveis do SLSA
SLSA (Supply-chain Levels for Software Artifacts) virou framework de referencia depois que Google e OpenSSF padronizaram a v1.0 em 2023. Nivel 1 e so ter build script versionado. Nivel 2 exige builder hospedado com proveniencia assinada. Nivel 3, que e onde queremos chegar em projetos sensiveis, exige isolamento do build, proveniencia nao falsificavel e hermetic builds onde o build nao tem acesso de rede nao controlado.
O salto decisivo esta entre o Nivel 2 e o 3: uma proveniencia que o proprio builder gera e assina nao pode ser falsificada por um desenvolvedor comprometido, porque ele nao controla o processo do builder. Para times que tambem cuidam de Dependency Confusion e Typosquatting: Defesa Pratica para Times Dev, SLSA fecha o lado do publisher, enquanto reserva de namespace e pinning de registry cobrem o lado do consumidor.
Proveniencia com slsa-github-generator
Na pratica usamos GitHub Actions com o reusable workflow slsa-framework/slsa-github-generator, que produz attestation in-toto assinada pelo proprio runner via OIDC. Essa attestation documenta de forma criptograficamente verificavel o commit fonte, os parametros de build e a versao da toolchain. Um atacante que comprometa um maintainer ainda precisa quebrar o builder do GitHub, o que aumenta o custo do ataque em ordens de magnitude.
O ponto de quebra mais comum aqui sao os builds nao-reproduziveis. Timestamps embutidos em binarios Go, caminhos absolutos e build IDs embutidos fazem dois builds do mesmo commit produzirem hashes diferentes. Use -trimpath, fixe SOURCE_DATE_EPOCH e pine a toolchain em um lockfile, senao sua proveniencia esta assinada mas nao e reproduzivel de forma verificavel.
Verificacao no lado do consumidor
Verificacao no consumidor e tao importante quanto assinatura no produtor. Em clusters Kubernetes usamos o admission controller policy-controller do Sigstore com ClusterImagePolicy exigindo que toda imagem em namespace producao tenha assinatura cosign valida, identidade OIDC do dominio basilisk.example e attestation SLSA do builder esperado. Configuramos fallback de soft-fail em staging para evitar quebrar deploy emergencial, e hard-fail em prod.
Tambem rodamos cosign verify-blob nos artefatos de cli baixados em estacoes de analista, integrado ao fluxo descrito em OPSEC para Pesquisadores de Seguranca: Modelo de Ameaca Pessoal, porque time de OffSec baixa muita ferramenta de origem incerta e isso vira vetor obvio. O principio: assine na origem, verifique no ponto de consumo, e nunca confie em um artefato so porque ele esta no seu registry.
Limites: takeover social e Scorecard
Onde a coisa quebra na pratica: builds nao-reproduziveis, timestamps embutidos em binarios Go, dependencias transitivas que mudam entre git pull e CI, e maintainers que recusam adotar 2FA. Pre-2.0 do XZ ja tinha sinais de takeover social do projeto que nenhuma assinatura captura: um contribuidor novo ganhando confianca rapido demais, pressao sobre o maintainer original, e commits com dados de teste ofuscados.
Por isso recomendamos combinar cosign+SLSA+SBOM com revisao periodica de maintainers criticos, scorecard do OpenSSF rodando semanalmente nas top 50 dependencias, e exercicios de red team que simulem comprometimento de maintainer, similar ao que fazemos em engajamentos descritos em Adversary Emulation com Caldera e MITRE ATT&CK em Lab Corporativo e Purple Team na Pratica: Construindo Ciclo de Feedback Red x Blue. Tecnologia sem processo de manutencao vira teatro caro.
Pinning de registry e digests imutaveis
Assinatura e proveniencia servem de pouco se seus deploys referenciam uma tag movel como :latest. Um :latest verificado ontem pode apontar para outra imagem hoje. Em producao, pine sempre no digest imutavel (image@sha256:...), nao numa tag. O digest e a impressao digital criptografica do conteudo exato da imagem; um digest pinado mais assinatura verificada dao uma cadeia sem cortes do commit fonte ate o container em execucao.
Complemente isso com um registry interno espelhado (Harbor, Artifactory) rodando um pull-through cache com regras de imutabilidade, para que uma tag upstream apagada ou sobrescrita nao quebre seus builds e um atacante nao consiga re-atar uma tag ja verificada. Combinado com uma allowlist de registries permitidos no admission controller, voce fecha a brecha por onde os ataques de dependency confusion se enfiam.
Rollout de 30 dias
Esta semana: adicione cosign sign keyless em um unico pipeline, gere SBOM CycloneDX com syft e publique como artefato OCI ao lado da imagem. Semana que vem: ligue o grype no PR com policy warn-only para calibrar seu ruido base de CVE. Semana tres: ative cosign verify obrigatorio em um namespace de staging com soft-fail. Semana quatro: vire para hard-fail em prod para um unico servico bem entendido e expanda a partir dali.
FAQ
Keyless exige acesso a internet no build? Sim, Fulcio e Rekor sao servicos online. Para ambientes air-gapped voce roda uma instancia privada do Sigstore (Fulcio, Rekor, Trillian) internamente ou volta ao cosign baseado em chave com a key guardada em um KMS/HSM. Keyless e a recomendacao padrao, nao um dogma.
Uma SBOM substitui o scan de vulnerabilidades? Nao. A SBOM e o inventario, o scanner e a analise contra ele. Uma SBOM sem rescan continuo contra feeds de CVE atuais envelhece em dias. Juntos dao uma foto consultavel e atual da sua superficie de ataque; sozinhos sao meias-medidas.
Como vender o custo para a gestao? Nao com argumentos de compliance, mas com a conta de tempo de resposta. Na ultima CVE grande do OpenSSL, times sem inventario SBOM levaram em media dias so para descobrir quais sistemas estavam afetados. Times com SBOM atada responderam a mesma pergunta em minutos e ja comecaram a corrigir. Essa diferenca se traduz direto em risco de queda e hora extra, e e exatamente isso que um dono de orcamento entende.
Takeaway pratico: comece pequeno e mensuravel. Em 30 dias voce tem base para exigir SLSA L2 de fornecedores e responder com evidencia quando um proximo XZ acontecer, porque ele vai acontecer. O custo de implementacao em um pipeline existente fica em torno de 2 a 4 horas de engenheiro; o custo de nao implementar e perguntar para clientes em call de domingo se voce assinou aquele container que esta minerando Monero em producao.


