CI/CD Seguro: Dependabot, SAST e DAST no GitHub
Introdução#
Vulnerabilidades não aparecem apenas em infraestrutura. Elas vêm de código inseguro, dependências desatualizadas e containers mal configurados. Enquanto você defende contra DDoS, aplicações vulneráveis podem ser exploradas diretamente por atacantes.
Neste artigo, você aprenderá a implementar 5 camadas de segurança automática em seu pipeline CI/CD usando ferramentas gratuitas do GitHub e serviços open source. Cada uma detecta um tipo diferente de risco: dependências vulneráveis, código inseguro, falhas lógicas e imagens de container comprometidas.
O melhor: tudo funciona sem custos adicionais se você usa GitHub Public ou temos opções gratuitas para repos privados.
⚠️ Atenção: Estas ferramentas detectam e alertam sobre problemas, mas não corrigem tudo automaticamente. Você ainda precisa revisar e aprovar cada correção. Segurança é um processo contínuo, não um checkbox.
Pré-requisitos#
Para implementar as medidas deste artigo, você precisa de:
- Repositório no GitHub (público ou privado)
- GitHub Actions habilitado (padrão em repos novos)
- Conhecimento básico de YAML (workflows GitHub Actions)
- Aplicação em Docker ou código C#/.NET (exemplos são específicos, mas conceitos aplicam a qualquer linguagem)
- Conta SonarCloud (gratuita para repos públicos e privados)
Repos privados com plano GitHub Pro/Team têm acesso a algumas ferramentas gratuitas de segurança. Repos públicos têm tudo gratuito.
1. Dependabot: Detectar Dependências Vulneráveis#
Dependabot é a primeira linha de defesa. Ele verifica automaticamente suas dependências contra bancos de dados de vulnerabilidades conhecidas (CVE) e abre pull requests com atualizações de segurança.
Exemplo real: Um pacote que você usa há 6 meses descobre uma falha crítica. Dependabot abre um PR automaticamente dentro de horas. Você revisa, testa e merge.
Ativar Dependabot no GitHub#
- Acesse Settings → Code security and analysis
- Ative Dependabot alerts e Dependabot security updates
- Pronto — Dependabot já está monitorando
Configuração Avançada: dependabot.yml#
Para controlar frequência de atualizações e agrupamento de PRs, crie .github/dependabot.yml:
version: 2
updates:
# Dependências do .NET (NuGet)
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "03:00"
allow:
- dependency-type: "all"
pull-request-branch-name:
separator: "/"
reviewers:
- "seu-usuario"
assignees:
- "seu-usuario"
open-pull-requests-limit: 10 # Máximo 10 PRs abertos ao mesmo tempo
# Docker images
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "04:00"
open-pull-requests-limit: 5
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "05:00"
# Python (se aplicável)
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "03:00"
allow:
- dependency-type: "direct" # Apenas dependências diretas, não transitivas
Automação: Auto-merge de Updates Seguras#
- Conta SonarCloud (gratuita para repos públicos e privados)
Se você quer que Dependabot merge automaticamente certas atualizações (patch versions, por exemplo):
name: Auto-merge Dependabot updates
on: pull_request
jobs:
auto-merge:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- uses: actions/checkout@v4
- name: Enable auto-merge para patches de segurança
run: |
gh pr merge --auto --squash "${{ github.event.pull_request.number }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Apenas merge automaticamente se o update é MINOR ou PATCH, não MAJOR
ℹ️ Informação: Dependabot detecta vulnerabilidades em tempo real. Em 2024, 42% de todos os PRs de segurança são abertos por ferramentas automáticas. Sem automação, você fica para trás.
2. GitHub Code Scanning: Análise Estática com CodeQL#
GitHub Code Scanning usa CodeQL (linguagem de query estática) para encontrar bugs de segurança e code smells em seu código. Funciona em repositórios públicos e privados (com GitHub Pro).
O que detecta: SQL injection, XSS, command injection, use-after-free, variáveis não inicializadas, e 300+ outros padrões inseguros.
Ativar Code Scanning#
- Acesse Settings → Code security and analysis
- Ative GitHub Advanced Security (se disponível na sua conta)
- Code Scanning é ativado automaticamente
Workflow Automático com CodeQL#
GitHub gera automaticamente um workflow .github/workflows/github-code-scanning.yml. Customize se necessário:
name: CodeQL Analysis
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
schedule:
- cron: '0 3 * * 0' # Toda segunda-feira às 03:00 UTC
jobs:
analyze:
name: CodeQL Analyze
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'csharp', 'python', 'javascript' ] # Ajuste conforme seu projeto
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: security-extended # Usar queries de segurança estendidas
- name: Autobuild (C#)
if: matrix.language == 'csharp'
run: |
dotnet build --configuration Release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
Interpretar Resultados#
Resultados aparecem em Security → Code scanning alerts:
- Critical: Falhas que afetam segurança (SQL injection, RCE)
- High: Bugs que podem ser explorados
- Medium/Low: Práticas ruins ou technical debt
Desabilitar Alertas de Forma Segura#
Nem todo alerta é actionable. Se você confirma que um alerta é false positive:
# No código comentado:
// lgtm [cs/sql-injection] — validamos o input antes de usar em query
var query = $"SELECT * FROM users WHERE id = {userId}";
📝 Exemplo: CodeQL encontrou um SQL injection potencial em um relatório. Resultado: descobriu que você estava concatenando IDs de usuário sem validação. Depois de adicionar parametrização, o alerta desapareceu.
3. SonarCloud: Análise SAST Profunda e Code Quality#
SonarCloud vai além de segurança — mede qualidade de código, cobertura de testes, duplicação e code smells. É gratuito para repos públicos e privados (com limitações).
O que detecta além de segurança: bugs, code smells, code coverage, technical debt, falhas de design.
Setup: Conectar GitHub a SonarCloud#
- Acesse SonarCloud.io
- Faça login com sua conta GitHub
- Selecione os repositórios a analisar
- Gere um token em My Account → Security → Generate Tokens
Workflow com SonarCloud#
Adicione .github/workflows/sonarcloud.yml:
name: SonarCloud Analysis
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
jobs:
sonarcloud:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Necessário para análise de qualidade
- name: Set up .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore NuGet packages
run: dotnet restore
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Gere em SonarCloud
- name: Upload coverage to Codecov (opcional)
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage.xml
Arquivo sonar-project.properties#
Crie na raiz do seu projeto:
sonar.projectKey=seu-usuario_seu-projeto
sonar.organization=seu-usuario
# C#
sonar.sources=src
sonar.exclusions=**/bin/**,**/obj/**,**/tests/**
sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml
# Testes
sonar.tests=tests
sonar.test.inclusions=**/Tests/**
# Code Coverage
sonar.coverage.exclusions=**/Tests/**
Badges de Qualidade#
Adicione ao README para mostrar status:
[](https://sonarcloud.io/dashboard?id=seu-usuario_seu-projeto)
[](https://sonarcloud.io/dashboard?id=seu-usuario_seu-projeto)
[](https://sonarcloud.io/dashboard?id=seu-usuario_seu-projeto)
ℹ️ Informação: SonarCloud analisa Pull Requests automaticamente e coloca comentários no código com sugestões. Desenvolvedores recebem feedback em tempo real, não no final do sprint.
4. OWASP ZAP: Testes Dinâmicos de Segurança (DAST)#
OWASP ZAP (Zed Attack Proxy) é um teste dinâmico — ele acessa sua aplicação como um atacante e procura vulnerabilidades em tempo de execução.
Diferença SAST vs DAST:
- SAST (Code Scanning, SonarCloud): analisa código-fonte sem executar
- DAST (ZAP): executa a aplicação e ataca como um hacker real
Workflow com OWASP ZAP#
Adicione .github/workflows/owasp-zap.yml:
name: OWASP ZAP Scan
on:
push:
branches: [ main, develop ]
schedule:
- cron: '0 2 * * 0' # Domingo às 2 AM
jobs:
zap-scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t seu-app:latest .
- name: Start application
run: |
docker run -d --name app -p 8080:80 seu-app:latest
sleep 10 # Aguarde app iniciar
- name: Run OWASP ZAP scan
uses: zaproxy/[email protected]
with:
target: 'http://localhost:8080'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a' # Análise completa
issue_title: 'OWASP ZAP: Vulnerabilidades encontradas'
- name: Upload ZAP report
if: always()
uses: actions/upload-artifact@v3
with:
name: zap-report
path: report_html.html
- name: Comment on PR (se DAST falhar)
if: failure()
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚨 OWASP ZAP encontrou vulnerabilidades. Veja o relatório em Artifacts.'
})
- name: Cleanup
if: always()
run: docker stop app && docker rm app
Configuração de Regras (.zap/rules.tsv)#
Customize quais vulnerabilidades ZAP deve blocar:
# rule_id alert_title threshold enabled
10000 Information IGNORE true
10001 Informational IGNORE true
40014 Cross Site Request Forgery HIGH true
40016 Cross Site Scripting (Reflected) MEDIUM true
40017 Cross Site Scripting (Stored) HIGH true
90024 SQL Injection CRITICAL true
90025 LDAP Injection HIGH true
Rodando ZAP Localmente para Teste#
Antes de colocar em CI/CD, teste localmente:
# Instalar ZAP
docker pull owasp/zap2docker-stable
# Executar baseline scan
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t http://seu-app.local:80 \
-r report.html
⚠️ Atenção: DAST só funciona se sua aplicação está rodando. Certifique-se de que o Docker build e start sejam rápidos. Scans podem levar 5-10 minutos.
5. Trivy: Scanning de Container e Dependências#
Trivy verifica imagens Docker e arquivos de dependência em busca de vulnerabilidades conhecidas. É rápido (segundos), agnóstico a linguagem e integra bem em CI/CD.
O que escaneia: imagens Docker, arquivos requirements.txt, package.json, *.csproj, etc.
Workflow com Trivy#
Adicione .github/workflows/trivy.yml:
name: Trivy Vulnerability Scan
on:
push:
branches: [ main, develop ]
paths: [ 'Dockerfile', '*.csproj', 'requirements.txt', 'package.json' ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 1 * * 0' # Domingo à 1 AM
jobs:
trivy-scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scan (filesystem)
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs' # Filesystem scan
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'HIGH,CRITICAL'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
- name: Build Docker image (se houver Dockerfile)
if: hashFiles('Dockerfile') != ''
run: docker build -t seu-app:${{ github.sha }} .
- name: Scan Docker image with Trivy
if: hashFiles('Dockerfile') != ''
uses: aquasecurity/trivy-action@master
with:
input: 'seu-app:${{ github.sha }}'
format: 'table'
exit-code: '1' # Fail if vulnerabilities found
severity: 'CRITICAL' # Apenas critical, não warning
Executar Trivy Localmente#
# Instalar Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# Escanear arquivo local
trivy fs .
# Escanear imagem Docker
trivy image seu-app:latest
# Apenas vulnerabilidades críticas
trivy image --severity CRITICAL seu-app:latest
Interpretando Saída Trivy#
vuln-id severity package
CVE-2024-1234 CRITICAL openssl/1.0.2
CVE-2024-5678 HIGH curl/7.32.0
Cada CVE tem um link para detalhes. Se for crítico, você deve atualizar o pacote imediatamente.
📝 Exemplo: Trivy encontrou CVE-2024-1234 em uma imagem base Ubuntu. Você atualiza a imagem, reconstrói e o scan passa. Tudo em menos de 1 minuto.
6. Exemplo Prático: Workflow Completo em CI/CD#
Aqui está um workflow que roda todas as 5 camadas de segurança em cada push:
name: Segurança Completa
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
# 1. Dependabot já roda automaticamente (integrado no GitHub)
# 2. CodeQL
codeql:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v2
with:
languages: csharp
- run: dotnet build --configuration Release
- uses: github/codeql-action/analyze@v2
# 3. SonarCloud
sonarcloud:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- run: dotnet restore
- uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# 4. Trivy (filesystem + Docker image)
trivy:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'sarif'
output: 'trivy-results.sarif'
- uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
# 5. OWASP ZAP (apenas em main)
dast:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- run: docker build -t seu-app:latest .
- run: docker run -d --name app -p 8080:80 seu-app:latest && sleep 10
- uses: zaproxy/[email protected]
with:
target: 'http://localhost:8080'
rules_file_name: '.zap/rules.tsv'
- if: always()
run: docker stop app
Tempo Total de Execução#
- CodeQL: ~3 minutos
- SonarCloud: ~4 minutos
- Trivy: ~30 segundos
- OWASP ZAP: ~8 minutos (apenas em main)
Total: ~10-15 minutos por push (sem paralelização). Com paralelização no GitHub Actions, rodam em paralelo.
📖 Artigo completo com exemplos de código: CI/CD Seguro: Dependabot, SAST e DAST no GitHub