Até a PR chore(security) que introduziu este arquivo, o repositório
versionava os seguintes artefatos sensíveis:
| Artefato | Conteúdo exposto |
|---|---|
.env |
DB_PASSWORD, PGPASSWORD, BOOTSTRAP_SECRET e demais variáveis reais |
mcp/postgres/.env |
DATABASE_URL com senha do banco |
keys/m4vNIvWOllU.pem |
chave privada RSA usada para assinar todos os JWTs (kid m4vNIvWOllU) |
Esses arquivos foram removidos do índice (git rm --cached), mas continuam
recuperáveis em qualquer commit anterior. Remover do tracking não é
rotacionar. Enquanto a rotação abaixo não for feita, assuma que qualquer pessoa
com acesso ao histórico (clones, forks, CI caches) consegue forjar tokens
válidos e acessar o banco.
- Chave RSA do JWT
- Em produção, gerar uma nova chave no volume apontado por
JWT_RSA_KEYS_DIRcom nome lexicograficamente maior quem4vNIvWOllU(ex.:openssl genrsa -out "$JWT_RSA_KEYS_DIR/zz-$(date +%Y%m%d).pem" 2048) ou simplesmente remover o.pemantigo e deixar a aplicação gerar outra. - Remover
m4vNIvWOllU.pemdo volume. Como não há revogação de tokens, todo access/refresh token emitido com a chave vazada deve passar a falhar na validação — isso só acontece quando o.pemantigo some do diretório. - Clientes que fazem cache do JWKS precisam recarregar as chaves.
- Em produção, gerar uma nova chave no volume apontado por
BOOTSTRAP_SECRET: gerar novo valor (openssl rand -base64 32), atualizar no ambiente da API e em todos os clientes que chamam/applications/synce/password-reset/*via HMAC.- Senha do PostgreSQL:
ALTER USER <usuario> WITH PASSWORD '<nova>', atualizarDB_PASSWORD/PGPASSWORD/DATABASE_URLem todos os ambientes e ferramentas (incluindomcp/postgres/.envlocal). - Revisar logs de acesso ao banco e à API no período de exposição.
Reescrever histórico é destrutivo e exige coordenação com todos que têm clone. Fazer em uma janela combinada, depois da rotação acima:
# 1. Instalar git-filter-repo (https://github.com/newren/git-filter-repo)
brew install git-filter-repo # ou: pip install git-filter-repo
# 2. Clonar uma cópia fresca (filter-repo recusa rodar em clone "sujo")
git clone --mirror git@github.com:theretechlabs/retech-auth-api.git auth-api-purge.git
cd auth-api-purge.git
# 3. Remover os caminhos sensíveis de TODOS os commits
git filter-repo --invert-paths \
--path .env \
--path mcp/postgres/.env \
--path keys/m4vNIvWOllU.pem \
--path-glob 'keys/*.pem' \
--path mcp/postgres/node_modules
# 4. Conferir que nada sobrou
git log --all --diff-filter=A --name-only --format= | grep -E '\.env$|\.pem$' || echo OK
# 5. Forçar push de todas as refs (branches e tags)
git push --force --mirror
# 6. No GitHub: Settings > General > "Danger Zone" não basta — abrir ticket no
# suporte GitHub pedindo limpeza de objetos órfãos/caches de PR, e pedir que
# todos os colaboradores apaguem clones antigos e clonem novamente.Não abra issue pública. Envie e-mail para suporte@theretech.com.br com
assunto [SECURITY] retech-auth-api, incluindo passos de reprodução, impacto
e versão/commit afetado. Respondemos em até 5 dias úteis e combinamos prazo de
divulgação coordenada. Se precisar de canal cifrado, solicite a chave PGP na
primeira mensagem.
Como a chave RSA é gerada, armazenada (volume em /app/keys) e o roadmap de rotação/backup/KMS:
docs/roadmap-chaves-jwt.md.
Itens identificados em revisão e não corrigidos nesta PR (que se limitou a higiene de repositório e CI, sem alterar comportamento). Ordem aproximada de prioridade.
| # | Item | Onde | Risco |
|---|---|---|---|
| 0 | Dependências vulneráveis. govulncheck acusa GO-2025-3553 em github.com/golang-jwt/jwt/v5@v5.2.0 (alocação excessiva de memória ao parsear header; fix em v5.2.2), alcançável a partir de ValidateToken. Além disso há 10 vulnerabilidades em pacotes importados e 22 em módulos requeridos (gin v1.9.1, x/net, x/crypto etc.) que o código não parece chamar. Abrir PR dedicada de go get -u + go mod tidy e, só então, remover o continue-on-error do job govulncheck em .github/workflows/ci.yml. |
go.mod, internal/application/service/jwt_service.go |
Alto |
| 1 | typ, iss, aud e jti.typ (access/refresh), iss (JWT_ISSUER), aud (= application_code) e jti; iss é validado na leitura. |
internal/application/service/jwt_service.go |
|
| 2 | AuthMiddleware exige typ=access; /refresh e /logout exigem typ=refresh com jti. |
jwt_service.go, auth_middleware.go, refresh_token_usecase.go |
|
| 3 | refresh_tokens (migration 008) guarda o jti; refresh rotaciona (um uso), reuso revoga a família; POST /v1/logout revoga um ou todos. Access token continua valendo até o exp (curto). |
logout_usecase.go, refresh_token_usecase.go |
|
| 4 | O grupo protected só exige JWT válido; não há verificação de role/permission em /applications, /roles, /permissions nem em POST /users/:id/password/reset. Qualquer usuário autenticado de qualquer aplicação pode administrar RBAC e resetar senhas de outros. |
cmd/api/main.go (grupo protected), handlers em internal/infrastructure/http/handler/ |
Crítico |
| 5 | ListUsersUseCase filtra apenas por application_id; não aplica tenant_id do token. Usuários de um tenant enxergam todos os usuários da aplicação. |
internal/application/usecase/list_users_usecase.go, internal/infrastructure/repository/postgres_user_repository.go |
Alto |
| 6 | CORS: CORS_ALLOWED_ORIGINS=* ecoa a Origin do request junto de Access-Control-Allow-Credentials: true, o que equivale a permitir credenciais para qualquer origem. |
internal/infrastructure/http/middleware/cors_middleware.go |
Médio |
| 7 | /applications/sync e /password-reset/* (antes, qualquer usuário autenticado de qualquer aplicação podia chamar essas rotas sem HMAC). Agora: HMAC obrigatório, 401 genérico, detalhe só no log. |
internal/infrastructure/http/middleware/sync_middleware.go |
|
| 8 | GetAllPublicKeysJWK segura mu.RLock() e chama GetPublicKeyJWK → GetPublicKey → GetCurrentKeyID, que fazem RLock() de novo. RLock reentrante trava se um Lock() (ex.: RotateKey) estiver na fila. |
internal/application/service/rsa_key_service.go |
Médio |
| 9 | /authenticate por IP e por e-mail, /refresh + /logout por IP, /password-reset/* por IP (RATE_LIMIT_*). IP = último hop do X-Forwarded-For. Sem lockout de conta ainda. |
middleware/ratelimit.go |
|
| 10 | http.Server sem timeouts e sem graceful shutdown. |
cmd/api/main.go |
|
| 11 | O kid do JWT é usado como nome de arquivo ao carregar chaves; se algum dia vier de input externo precisa de sanitização de path. Hoje é derivado só de arquivos locais. |
rsa_key_service.go |
Baixo |
| 12 | Migrations rodam automaticamente no boot da API com o mesmo usuário de runtime (precisa DDL). Preferível usuário separado para migração. | cmd/api/main.go, internal/infrastructure/migration/ |
Baixo |