Durante décadas, a segurança foi tratada como uma fase — algo que acontecia depois que o software estava pronto, executado por um time separado que revisava o código antes do release. Esse modelo produziu dois problemas persistentes: vulnerabilidades encontradas tarde no ciclo de desenvolvimento são ordens de grandeza mais caras de corrigir, e times de segurança operando como gargalhos de aprovação inevitavelmente ficaram para trás da velocidade de entrega que o DevOps trouxe.
O DevSecOps é a resposta a esses dois problemas. O princípio central é deslocar a segurança para a esquerda — shift left — integrando verificações de segurança em cada etapa do ciclo de desenvolvimento, do editor de código ao ambiente de produção. Não como um gargalo, mas como feedback automático que o desenvolvedor recebe no mesmo fluxo em que recebe feedback de testes.
A segurança eficaz em pipelines modernos opera em camadas: código-fonte, dependências, imagens de container, infraestrutura como código, configurações do cluster e comportamento em runtime. Este artigo percorre cada uma dessas camadas com as ferramentas e práticas que as tornam automatizáveis.
Camada 1: Segurança no Código-Fonte
Análise Estática com Semgrep
O Semgrep é um analisador estático de código que detecta padrões de segurança diretamente no código-fonte — injeção SQL, segredos hardcoded, uso de funções inseguras, lógica de autenticação defeituosa. Opera sobre o AST do código, não sobre texto, o que elimina falsos positivos comuns em ferramentas baseadas em regex:
# .github/workflows/segurança-código.yml
name: Análise de Segurança do Código
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
semgrep:
runs-on: ubuntu-latest
container:
image: returntocorp/semgrep:latest
steps:
- uses: actions/checkout@v4
- name: Executa Semgrep
run: |
semgrep scan \
--config=p/nodejs \
--config=p/javascript \
--config=p/secrets \
--config=p/owasp-top-ten \
--error \
--json \
--output=semgrep-results.json \
--metrics=off \
.
- name: Publica resultados como SARIF
if: always()
run: |
semgrep scan \
--config=p/nodejs \
--config=p/secrets \
--sarif \
--output=semgrep.sarif \
--metrics=off \
.
- name: Upload resultados para GitHub Security
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep.sarif
Regras customizadas do Semgrep para padrões específicos do projeto:
# .semgrep/regras-customizadas.yml
rules:
# Detecta queries SQL construídas por concatenação de strings
- id: sql-injection-concatenacao
patterns:
- pattern: |
$QUERY = "..." + $USER_INPUT
- pattern-either:
- pattern: db.query($QUERY, ...)
- pattern: pool.query($QUERY, ...)
message: |
Query SQL construída por concatenação detectada.
Use queries parametrizadas: db.query('SELECT * FROM users WHERE id = $1', [id])
languages: [javascript, typescript]
severity: ERROR
# Detecta uso de algoritmos de hash inseguros para senhas
- id: hash-inseguro-para-senha
patterns:
- pattern-either:
- pattern: crypto.createHash('md5')
- pattern: crypto.createHash('sha1')
- pattern: crypto.createHash('sha256')
- pattern-not-inside: |
# Ignora uso legítimo para checksums de arquivos
fs.$READ(...)
message: |
Algoritmo de hash inseguro para senhas.
Use bcrypt, argon2 ou scrypt para hashing de senhas.
languages: [javascript, typescript]
severity: ERROR
# Detecta JWT sem verificação de algoritmo
- id: jwt-algorithm-none
pattern: |
jwt.verify($TOKEN, $SECRET, { algorithms: [..., "none", ...] })
message: |
O algoritmo "none" no JWT permite tokens não assinados.
Remova "none" da lista de algoritmos permitidos.
languages: [javascript, typescript]
severity: ERROR
# Detecta logging de informações sensíveis
- id: log-dados-sensiveis
patterns:
- pattern-either:
- pattern: console.log(..., $REQ.body.password, ...)
- pattern: logger.info(..., $REQ.body.password, ...)
- pattern: console.log(..., $REQ.headers.authorization, ...)
message: |
Dados sensíveis sendo logados.
Nunca logue senhas, tokens ou dados de cartão.
languages: [javascript, typescript]
severity: WARNING
Detecção de Segredos com Gitleaks
O Gitleaks detecta segredos — chaves de API, tokens, senhas — no histórico do Git, incluindo commits antigos:
# .github/workflows/detectar-segredos.yml
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Histórico completo para escanear todos os commits
- name: Executa Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Configuração para reduzir falsos positivos:
# .gitleaks.toml
title = "Configuração Gitleaks"
[extend]
useDefault = true
[[allowlist]]
description = "Ignora arquivos de teste com dados falsos"
paths = [
'''tests/fixtures/.*''',
'''\.env\.example''',
'''\.env\.test''',
]
[[allowlist]]
description = "Ignora padrões de placeholder"
regexes = [
'''EXAMPLE_KEY_.*''',
'''your[-_]api[-_]key''',
'''<YOUR_.*>''',
'''TODO.*key''',
]
Camada 2: Segurança em Dependências
Software Composition Analysis com npm audit e Snyk
Aplicações modernas dependem de centenas de pacotes de terceiros. Cada pacote é uma superfície de ataque potencial — e vulnerabilidades em dependências transitivas são frequentemente invisíveis até que causem um incidente.
# No pipeline de CI — verificação de dependências
dependencias:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Instala dependências
run: npm ci
- name: Auditoria de segurança npm
run: |
# Falha se houver vulnerabilidades de alta criticidade
npm audit --audit-level=high --json > audit-results.json || {
echo "Vulnerabilidades críticas encontradas:"
cat audit-results.json | jq '.vulnerabilities | to_entries[] |
select(.value.severity == "high" or .value.severity == "critical") |
{pacote: .key, severidade: .value.severity,
CVE: .value.via[0].url}'
exit 1
}
- name: Snyk para análise aprofundada
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: >
--severity-threshold=high
--fail-on=upgradable
--sarif-file-output=snyk.sarif
- name: Upload resultados Snyk
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: snyk.sarif
Renovate: Atualizações Automáticas de Dependências
O Renovate abre pull requests automaticamente quando novas versões de dependências são publicadas, incluindo informações de changelog e resultados de testes:
// renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
"security:openssf-scorecard"
],
"schedule": ["before 6am on Monday"],
"timezone": "America/Sao_Paulo",
"labels": ["dependências", "automated"],
"prConcurrentLimit": 10,
"packageRules": [
{
"matchUpdateTypes": ["patch"],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"matchUpdateTypes": ["major"],
"dependencyDashboardApproval": true
},
{
"matchPackageNames": ["express", "pg", "redis"],
"reviewers": ["time-backend"]
}
],
"vulnerabilityAlerts": {
"labels": ["segurança", "vulnerabilidade"],
"automerge": true
}
}
Camada 3: Segurança em Imagens de Container
Trivy: Scanning de Vulnerabilidades em Imagens
O Trivy escaneia imagens de container em busca de vulnerabilidades no sistema operacional base, bibliotecas de linguagens e configurações inseguras:
# Etapa de scanning no pipeline de build de imagem
scanning-imagem:
runs-on: ubuntu-latest
needs: build
steps:
- name: Escaneia imagem com Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/empresa/minha-api:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: '1'
ignore-unfixed: true
- name: Upload resultados Trivy
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
Dockerfile Seguro
# Dockerfile com práticas de segurança integradas
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
# Instala dependências antes de copiar o código
# Aproveita cache de camadas do Docker
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build
# Stage 2: Imagem final mínima
FROM node:20-alpine AS runtime
# Instala apenas o necessário para runtime
RUN apk add --no-cache \
dumb-init \
curl \
&& rm -rf /var/cache/apk/*
# Cria usuário não-root
RUN addgroup -g 1001 -S nodejs \
&& adduser -S nodejs -u 1001 -G nodejs
WORKDIR /app
# Copia apenas os artefatos necessários
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./
# Garante que o binário pertence ao root mas é executável pelo usuário
RUN chown -R nodejs:nodejs /app
USER nodejs
EXPOSE 3000
# Health check integrado na imagem
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# dumb-init garante propagação correta de sinais
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]
Camada 4: Segurança em Infraestrutura como Código
Checkov: Analisando Terraform e Kubernetes
O Checkov verifica arquivos de IaC em busca de configurações inseguras — buckets S3 públicos, security groups com 0.0.0.0/0, pods sem limites de recursos, containers rodando como root:
checkov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Executa Checkov no Terraform
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
output_format: sarif
output_file_path: checkov-terraform.sarif
skip_check: >
CKV_AWS_136,
CKV2_AWS_62
- name: Executa Checkov nos manifestos Kubernetes
uses: bridgecrewio/checkov-action@master
with:
directory: kubernetes/
framework: kubernetes
output_format: sarif
output_file_path: checkov-k8s.sarif
- name: Upload resultados
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: checkov-terraform.sarif
Arquivo de supressão documentada para exceções legítimas:
# .checkov-suppressions.yml
# Supressões documentadas de verificações do Checkov
# Cada supressão deve ter justificativa e data de revisão
suppressions:
- check_id: CKV_AWS_18
resource: aws_s3_bucket.cloudfront_logs
justification: >
Bucket de logs do CloudFront não precisa de access logging próprio
— logging circular não adiciona valor e aumenta custos.
revisar_em: "2025-06-01"
responsavel: time-infra
- check_id: CKV_K8S_8
resource: Deployment/cert-manager
justification: >
cert-manager requer capabilities específicas para gerenciar
certificados TLS no cluster. Exceção aprovada pelo time de segurança.
revisar_em: "2025-06-01"
responsavel: joao-silva
Camada 5: Runtime Security com Falco
As verificações anteriores são preventivas — detectam problemas antes do deploy. O Falco complementa a postura de segurança com detecção em runtime: monitora syscalls no kernel e gera alertas quando comportamentos suspeitos são detectados em produção.
# kubernetes/falco/falco-rules.yml
customRules:
regras-empresa.yaml: |-
# Detecta execução de shell dentro de containers de aplicação
- rule: Shell em container de aplicação
desc: Detecta abertura de shell interativo em containers de aplicação
condition: >
spawned_process and
container and
proc.name in (shell_binaries) and
not proc.pname in (runc, containerd) and
k8s.ns.name = "producao"
output: >
Shell iniciado em container de produção
(usuario=%user.name container=%container.name
imagem=%container.image.repository:%container.image.tag
pod=%k8s.pod.name namespace=%k8s.ns.name
comando=%proc.cmdline)
priority: CRITICAL
tags: [container, shell, T1059]
# Detecta leitura de arquivos sensíveis
- rule: Leitura de arquivo sensível em container
desc: Detecta tentativa de leitura de /etc/shadow ou /etc/passwd
condition: >
open_read and
container and
fd.name in (/etc/shadow, /etc/passwd, /etc/ssh/ssh_host_rsa_key) and
not proc.name in (sshd, passwd, shadow)
output: >
Arquivo sensível lido em container
(arquivo=%fd.name processo=%proc.name
pod=%k8s.pod.name namespace=%k8s.ns.name)
priority: WARNING
tags: [filesystem, credentials]
# Detecta conexões de rede inesperadas
- rule: Conexão de rede inesperada em container
desc: Detecta containers abrindo conexões para portas não padronizadas
condition: >
outbound and
container and
k8s.ns.name = "producao" and
not fd.sport in (80, 443, 5432, 6379, 53) and
not proc.name in (node, npm)
output: >
Conexão de rede inesperada
(processo=%proc.name porta_destino=%fd.sport
ip_destino=%fd.rip pod=%k8s.pod.name)
priority: WARNING
tags: [network, lateral-movement]
Camada 6: Gestão de Segredos com HashiCorp Vault
Para organizações com requisitos mais rigorosos de gestão de segredos — rotação automática, acesso auditado, segredos dinâmicos — o HashiCorp Vault oferece capacidades que vão além do AWS Secrets Manager:
# vault.tf — configuração do Vault no Kubernetes
resource "helm_release" "vault" {
name = "vault"
repository = "https://helm.releases.hashicorp.com"
chart = "vault"
namespace = "vault"
create_namespace = true
version = "0.27.0"
values = [yamlencode({
server = {
ha = {
enabled = true
replicas = 3
raft = {
enabled = true
setNodeId = true
}
}
auditStorage = {
enabled = true
storageClass = "gp3"
size = "10Gi"
}
dataStorage = {
enabled = true
storageClass = "gp3"
size = "20Gi"
}
}
injector = {
enabled = true
}
})]
}
Configurando segredos dinâmicos para o banco de dados — o Vault gera credenciais temporárias com tempo de vida limitado:
# Configura o backend de banco de dados no Vault
vault secrets enable database
vault write database/config/minha-api-db \
plugin_name=postgresql-database-plugin \
allowed_roles="minha-api-producao" \
connection_url="postgresql://{{username}}:{{password}}@db.interno.empresa.com:5432/producao" \
username="vault_root" \
password="senha_root_vault"
# Define uma role que gera credenciais com TTL de 1 hora
vault write database/roles/minha-api-producao \
db_name=minha-api-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
revocation_statements="REVOKE ALL ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
O Vault Agent Injector injeta as credenciais geradas diretamente no pod como variáveis de ambiente:
# Annotations no Deployment para injeção do Vault Agent
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "minha-api-producao"
vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/minha-api-producao"
vault.hashicorp.com/agent-inject-template-db-creds: |
{{- with secret "database/creds/minha-api-producao" -}}
DATABASE_URL=postgresql://{{ .Data.username }}:{{ .Data.password }}@db.interno.empresa.com:5432/producao
{{- end }}
Pipeline de Segurança Completo
A integração de todas as camadas em um único workflow garante que nenhuma etapa é pulada:
# .github/workflows/pipeline-segurança.yml
name: Pipeline de Segurança
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * 1' # Toda segunda-feira às 2h — scan completo
jobs:
segredos:
name: Detectar Segredos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
sast:
name: Análise Estática (SAST)
runs-on: ubuntu-latest
needs: segredos
steps:
- uses: actions/checkout@v4
- name: Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >
p/nodejs
p/secrets
p/owasp-top-ten
.semgrep/regras-customizadas.yml
sca:
name: Análise de Composição (SCA)
runs-on: ubuntu-latest
needs: segredos
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm audit --audit-level=high
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
container:
name: Segurança da Imagem
runs-on: ubuntu-latest
needs: [sast, sca]
steps:
- uses: actions/checkout@v4
- name: Constrói imagem
run: docker build -t minha-api:scan .
- name: Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: minha-api:scan
severity: CRITICAL,HIGH
exit-code: '1'
ignore-unfixed: true
iac:
name: Segurança da Infraestrutura
runs-on: ubuntu-latest
needs: segredos
steps:
- uses: actions/checkout@v4
- uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: terraform,kubernetes,dockerfile
relatorio:
name: Consolida Relatório de Segurança
runs-on: ubuntu-latest
needs: [sast, sca, container, iac]
if: always()
steps:
- name: Verifica status de todos os jobs
run: |
if [[ "${{ needs.sast.result }}" == "failure" ]] || \
[[ "${{ needs.sca.result }}" == "failure" ]] || \
[[ "${{ needs.container.result }}" == "failure" ]] || \
[[ "${{ needs.iac.result }}" == "failure" ]]; then
echo "::error::Falhas de segurança detectadas. Revisão obrigatória antes do merge."
exit 1
fi
echo "Todas as verificações de segurança passaram."
Métricas de Maturidade em DevSecOps
A maturidade de uma postura DevSecOps pode ser medida por indicadores concretos que evoluem ao longo do tempo:
Mean Time to Remediation (MTTR) de vulnerabilidades — quanto tempo leva desde a detecção de uma vulnerabilidade até a correção em produção. Times maduros medem isso em horas para criticalidades altas, não semanas.
Cobertura de scanning — percentual de repositórios, imagens e configurações de IaC cobertos por verificações automatizadas. O objetivo é 100% — sem exceções não documentadas.
Taxa de falsos positivos — verificações que geram muito ruído são ignoradas pelos desenvolvedores. Manter a taxa de falsos positivos baixa é tão importante quanto a cobertura.
Vulnerabilidades por categoria e origem — rastrear de onde vêm as vulnerabilidades (código próprio vs dependências vs configuração) direciona onde investir esforço de melhoria.
O Que Vem a Seguir
O próximo artigo completa a visão de segurança com compliance e auditoria — como mapear controles técnicos para frameworks regulatórios como LGPD, SOC2 e ISO 27001, como implementar logging de auditoria imutável e como preparar evidências de conformidade de forma automatizada.
Referências para Aprofundamento
Ferramentas - Semgrep Documentation — semgrep.dev — Documentação completa do Semgrep com guia de escrita de regras customizadas, biblioteca de regras da comunidade e integração com CI/CD. - Trivy Documentation — aquasecurity.github.io/trivy — Documentação do Trivy cobrindo scanning de imagens, filesystems, repositórios Git e configurações de IaC. - Falco Documentation — falco.org — Documentação oficial do Falco com guia de criação de regras, integração com Kubernetes e configuração de alertas.
Frameworks e referências - OWASP DevSecOps Guideline — owasp.org — Guia do OWASP para implementação de DevSecOps, cobrindo as melhores práticas para cada fase do ciclo de desenvolvimento. - NIST Secure Software Development Framework — csrc.nist.gov — Framework do NIST para desenvolvimento seguro de software, com práticas organizadas por fase e mapeamento para outros frameworks de segurança. - HashiCorp Vault Documentation — developer.hashicorp.com — Documentação completa do Vault, incluindo backends de segredos dinâmicos, políticas de acesso e integração com Kubernetes.