feat: implement security enhancements for Jitsi integration, including JWT token generation and automatic blocking of detected attacks, improving system resilience and user authentication
This commit is contained in:
166
scripts/REVISAO_CIBERSECURITY.md
Normal file
166
scripts/REVISAO_CIBERSECURITY.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Revisão e Melhorias do Módulo de Cibersecurity
|
||||
|
||||
**Data:** 12/01/2026
|
||||
**Módulo:** `/ti/cibersecurity`
|
||||
**Objetivo:** Garantir funcionamento seguro do sistema mesmo diante de ataques
|
||||
|
||||
## Resumo das Melhorias Implementadas
|
||||
|
||||
### ✅ 1. Controle de Acesso (Admin-Only)
|
||||
|
||||
**Problema identificado:** Qualquer usuário autenticado podia executar ações críticas de segurança (bloquear IPs, criar regras, deletar configurações).
|
||||
|
||||
**Solução implementada:**
|
||||
- Adicionado helper `assertAdmin()` em `packages/backend/convex/security.ts`
|
||||
- Todas as queries e mutations sensíveis agora exigem role `admin === true`
|
||||
- Removido parâmetro `usuarioId` do cliente; o ID é derivado do usuário autenticado
|
||||
- Criado `+page.server.ts` na rota `/ti/cibersecurity` para redirecionar não-admins
|
||||
|
||||
**Funções protegidas:**
|
||||
- `listarEventosSeguranca`, `obterVisaoCamadas`, `listarReputacoes`
|
||||
- `atualizarReputacaoIndicador`, `configurarRegraPorta`, `deletarRegraPorta`
|
||||
- `solicitarRelatorioSeguranca`, `deletarRelatorio`
|
||||
- `salvarAlertConfig`, `deletarAlertConfig`, `listarAlertConfigs`
|
||||
- `criarConfigRateLimit`, `atualizarConfigRateLimit`, `deletarConfigRateLimit`, `listarConfigsRateLimit`
|
||||
- `registrarAcaoIncidente`
|
||||
|
||||
### ✅ 2. Hardening de Endpoints Dev-Only
|
||||
|
||||
**Problema identificado:** Endpoints e mutations de teste estavam acessíveis em produção.
|
||||
|
||||
**Solução implementada:**
|
||||
- Criado helper `assertDevOnly()` que verifica:
|
||||
- Flag `SECURITY_DEV_TOOLS === 'true'`
|
||||
- URL contém `localhost` ou `127.0.0.1`
|
||||
- Deployment local (`anonymous-*`)
|
||||
- `NODE_ENV !== 'production'`
|
||||
- Endpoint HTTP `/security/rate-limit/seed-dev` retorna 404 em produção
|
||||
- Mutations `criarEventosTeste`, `limparEventosTeste`, `seedRateLimitDev` bloqueadas em produção
|
||||
|
||||
### ✅ 3. Enforcement Real de Blacklist e Rate Limit
|
||||
|
||||
**Problema identificado:** As políticas eram apenas registradas no banco, sem enforcement real no tráfego.
|
||||
|
||||
**Solução implementada:**
|
||||
- Criada mutation `enforceRequest` que verifica:
|
||||
1. **Blacklist de IPs:** Consulta `ipReputation` e bloqueia com 403 se `blacklist === true` e ativo
|
||||
2. **Rate limit por endpoint:** Aplica regras configuradas para o path específico
|
||||
3. **Rate limit por IP:** Aplica regras globais ou específicas por IP
|
||||
- Integrado no `hooks.server.ts` do SvelteKit para proteger `/api/auth/*`
|
||||
- Integrado no handler HTTP `/security/analyze` do Convex
|
||||
- Respostas consistentes: 403 (blacklist) ou 429 (rate limit) com header `Retry-After`
|
||||
|
||||
**Arquivos modificados:**
|
||||
- `packages/backend/convex/security.ts` - função `enforceRequest`
|
||||
- `packages/backend/convex/http.ts` - enforcement no `/security/analyze`
|
||||
- `apps/web/src/hooks.server.ts` - enforcement no `/api/auth/*`
|
||||
|
||||
### ✅ 4. Melhorias de UX/UI
|
||||
|
||||
**Problema identificado:**
|
||||
- Alerta sonoro falhava por autoplay policy
|
||||
- Uso de `confirm()`/`alert()` nativos (inconsistente)
|
||||
- Componente muito grande (difícil manter)
|
||||
|
||||
**Solução implementada:**
|
||||
- **Alerta sonoro:** Agora requer interação do usuário (toggle) para ativar `AudioContext`
|
||||
- **Confirmações:** Substituído `confirm()` por modal `<dialog>` nativo com estado reativo
|
||||
- **Feedback:** Substituído `alert()` por sistema de feedback já existente (`feedback` state)
|
||||
|
||||
**Arquivos modificados:**
|
||||
- `apps/web/src/lib/components/ti/CybersecurityWizcard.svelte`
|
||||
|
||||
## Funcionalidades Validadas
|
||||
|
||||
### ✅ Dashboard Principal
|
||||
- ✅ Threat Matrix (gráfico de camadas) - 6h de dados
|
||||
- ✅ Feed de eventos em tempo real com filtros (severidade/tipo)
|
||||
- ✅ Contador de novos eventos
|
||||
- ✅ Lista negra (blacklist) com ações rápidas
|
||||
- ✅ Regras de porta (CRUD completo)
|
||||
- ✅ Relatórios refinados (solicitar, listar, imprimir PDF, excluir)
|
||||
- ✅ Rate limiting avançado (CRUD + ativar/desativar)
|
||||
- ✅ Alertas e notificações (configurações múltiplas, email/chat)
|
||||
|
||||
### ✅ Backend (Convex)
|
||||
- ✅ Queries protegidas (admin-only)
|
||||
- ✅ Mutations protegidas (admin-only)
|
||||
- ✅ Enforcement de blacklist
|
||||
- ✅ Enforcement de rate limit
|
||||
- ✅ Detecção automática de ataques (SQLi, XSS, DDoS, etc.)
|
||||
- ✅ Sistema de reputação de IPs
|
||||
- ✅ Alertas automáticos (email/chat)
|
||||
|
||||
## Pontos de Atenção
|
||||
|
||||
### ⚠️ Enforcement de Rate Limit
|
||||
- O enforcement atual funciona para:
|
||||
- Endpoints HTTP do Convex (`/security/analyze`)
|
||||
- Rotas `/api/auth/*` do SvelteKit
|
||||
- **Recomendação:** Para proteção completa, considere:
|
||||
- Proxy reverso (Nginx/Traefik) com rate limiting
|
||||
- WAF (Web Application Firewall) na borda
|
||||
- Cloudflare ou similar para DDoS protection
|
||||
|
||||
### ⚠️ Testes Automatizados
|
||||
- Script existente: `scripts/teste_seguranca.py`
|
||||
- **Recomendação:** Executar periodicamente para validar:
|
||||
- Detecção de ataques (SQLi, XSS, brute force, DDoS)
|
||||
- Bloqueio de IPs na blacklist
|
||||
- Rate limiting funcionando
|
||||
|
||||
### ⚠️ Ambiente de Produção
|
||||
- Endpoints dev-only estão protegidos, mas verifique:
|
||||
- `NODE_ENV` está configurado corretamente
|
||||
- `CONVEX_DEPLOYMENT` não começa com `anonymous-` em produção
|
||||
- Flag `SECURITY_DEV_TOOLS` não está definida em produção
|
||||
|
||||
## Como Testar
|
||||
|
||||
### 1. Teste de Autorização
|
||||
```bash
|
||||
# Como usuário não-admin, tentar acessar /ti/cibersecurity
|
||||
# Esperado: redirecionamento para /ti
|
||||
```
|
||||
|
||||
### 2. Teste de Enforcement
|
||||
```bash
|
||||
# Bloquear um IP via painel
|
||||
# Tentar fazer login com esse IP
|
||||
# Esperado: 403 Forbidden
|
||||
```
|
||||
|
||||
### 3. Teste de Rate Limit
|
||||
```bash
|
||||
# Configurar rate limit baixo (ex: 5 req/20s) para /api/auth/sign-in/email
|
||||
# Fazer 6 tentativas de login rapidamente
|
||||
# Esperado: 429 Too Many Requests na 6ª tentativa
|
||||
```
|
||||
|
||||
### 4. Teste de Detecção de Ataques
|
||||
```bash
|
||||
python scripts/teste_seguranca.py --teste brute_force
|
||||
python scripts/teste_seguranca.py --teste sql_injection
|
||||
python scripts/teste_seguranca.py --teste ddos
|
||||
```
|
||||
|
||||
## Próximos Passos Recomendados
|
||||
|
||||
1. **Monitoramento:** Implementar dashboard de métricas em tempo real
|
||||
2. **Automação:** Cron job para expirar bloqueios temporários automaticamente
|
||||
3. **Integração:** Conectar com WAF/proxy reverso para enforcement na borda
|
||||
4. **Testes E2E:** Criar suite de testes automatizados para validação contínua
|
||||
5. **Documentação:** Adicionar guia de uso para equipe TI
|
||||
|
||||
## Arquivos Modificados
|
||||
|
||||
- `packages/backend/convex/security.ts` - Guards de admin, enforcement, dev-only
|
||||
- `packages/backend/convex/http.ts` - Enforcement no endpoint HTTP
|
||||
- `apps/web/src/hooks.server.ts` - Enforcement no SvelteKit
|
||||
- `apps/web/src/routes/(dashboard)/ti/cibersecurity/+page.server.ts` - Guard de rota (novo)
|
||||
- `apps/web/src/lib/components/ti/CybersecurityWizcard.svelte` - UX melhorada
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ Implementação completa
|
||||
**Próxima revisão:** Após testes em ambiente de staging/produção
|
||||
526
scripts/configurar-jitsi.sh
Executable file
526
scripts/configurar-jitsi.sh
Executable file
@@ -0,0 +1,526 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script de configuração interativa do Jitsi Meet para SGSE
|
||||
# Este script facilita a configuração inicial do Jitsi no sistema SGSE
|
||||
|
||||
set -e
|
||||
|
||||
# Cores para output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Variáveis
|
||||
DOMAIN=""
|
||||
APP_ID=""
|
||||
ROOM_PREFIX=""
|
||||
USE_HTTPS=false
|
||||
ACCEPT_SELF_SIGNED=false
|
||||
JWT_SECRET=""
|
||||
JWT_AUDIENCE=""
|
||||
JWT_ISSUER=""
|
||||
AMBIENTE=""
|
||||
TEST_ONLY=false
|
||||
VALIDATE_ONLY=false
|
||||
NON_INTERACTIVE=false
|
||||
|
||||
# Função para imprimir mensagens
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# Função para validar formato de domínio
|
||||
validar_dominio() {
|
||||
local dominio=$1
|
||||
if [[ -z "$dominio" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Padrão para localhost:porta
|
||||
if [[ $dominio =~ ^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:[0-9]+)?$ ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Padrão para FQDN
|
||||
if [[ $dominio =~ ^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:[0-9]+)?$ ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Função para validar App ID
|
||||
validar_app_id() {
|
||||
local app_id=$1
|
||||
if [[ -z "$app_id" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ $app_id =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Função para validar JWT Secret
|
||||
validar_jwt_secret() {
|
||||
local secret=$1
|
||||
if [[ -z "$secret" ]]; then
|
||||
return 0 # Opcional
|
||||
fi
|
||||
|
||||
if [[ ${#secret} -lt 16 ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Função para testar conectividade
|
||||
testar_conectividade() {
|
||||
local dominio=$1
|
||||
local use_https=$2
|
||||
|
||||
print_info "Testando conectividade com $dominio..."
|
||||
|
||||
# Extrair host e porta
|
||||
local host=$(echo $dominio | cut -d: -f1)
|
||||
local porta=$(echo $dominio | cut -d: -f2)
|
||||
|
||||
if [[ -z "$porta" ]]; then
|
||||
if [[ "$use_https" == "true" ]]; then
|
||||
porta=443
|
||||
else
|
||||
porta=80
|
||||
fi
|
||||
fi
|
||||
|
||||
local protocol="http"
|
||||
if [[ "$use_https" == "true" ]]; then
|
||||
protocol="https"
|
||||
fi
|
||||
|
||||
local url="$protocol://$host:$porta/http-bind"
|
||||
|
||||
# Testar conexão
|
||||
if command -v curl &> /dev/null; then
|
||||
local response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>&1 || echo "000")
|
||||
if [[ "$response" == "200" ]] || [[ "$response" == "405" ]] || [[ "$response" == "000" ]]; then
|
||||
# 405 é esperado (Method Not Allowed para GET em /http-bind)
|
||||
print_success "Servidor acessível em $url"
|
||||
return 0
|
||||
else
|
||||
print_warning "Servidor retornou status $response (pode ser normal para /http-bind)"
|
||||
return 0
|
||||
fi
|
||||
elif command -v wget &> /dev/null; then
|
||||
if wget --spider --timeout=10 "$url" 2>&1 | grep -q "200 OK\|405\|connected"; then
|
||||
print_success "Servidor acessível em $url"
|
||||
return 0
|
||||
else
|
||||
print_warning "Não foi possível verificar conectividade (wget)"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_warning "curl ou wget não encontrado. Pulando teste de conectividade."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Função para validar certificado SSL
|
||||
validar_certificado_ssl() {
|
||||
local dominio=$1
|
||||
|
||||
if [[ ! "$dominio" =~ ^localhost ]]; then
|
||||
print_info "Validando certificado SSL para $dominio..."
|
||||
|
||||
local host=$(echo $dominio | cut -d: -f1)
|
||||
local porta=$(echo $dominio | cut -d: -f2)
|
||||
|
||||
if [[ -z "$porta" ]]; then
|
||||
porta=443
|
||||
fi
|
||||
|
||||
if command -v openssl &> /dev/null; then
|
||||
local cert_info=$(echo | openssl s_client -connect "$host:$porta" -servername "$host" 2>&1 | grep -A 2 "Certificate chain\|Verify return code")
|
||||
if echo "$cert_info" | grep -q "Verify return code: 0"; then
|
||||
print_success "Certificado SSL válido"
|
||||
return 0
|
||||
else
|
||||
print_warning "Certificado SSL não válido ou autoassinado"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_warning "openssl não encontrado. Pulando validação de certificado."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Função para gerar JWT Secret
|
||||
gerar_jwt_secret() {
|
||||
if command -v openssl &> /dev/null; then
|
||||
openssl rand -hex 32
|
||||
elif [[ -r /dev/urandom ]]; then
|
||||
head -c 32 /dev/urandom | base64 | tr -d '\n' | head -c 64
|
||||
else
|
||||
print_error "Não foi possível gerar JWT Secret. Instale openssl ou forneça manualmente."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Função para ler entrada do usuário
|
||||
ler_entrada() {
|
||||
local prompt=$1
|
||||
local default=$2
|
||||
local var_name=$3
|
||||
|
||||
if [[ "$NON_INTERACTIVE" == "true" ]]; then
|
||||
if [[ -n "$default" ]]; then
|
||||
eval "$var_name='$default'"
|
||||
return
|
||||
else
|
||||
print_error "Modo não-interativo requer valor para: $prompt"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local input
|
||||
if [[ -n "$default" ]]; then
|
||||
read -p "$(echo -e ${BLUE}$prompt${NC} [padrão: $default]): " input
|
||||
eval "$var_name=\${input:-$default}"
|
||||
else
|
||||
read -p "$(echo -e ${BLUE}$prompt${NC}): " input
|
||||
eval "$var_name='$input'"
|
||||
fi
|
||||
}
|
||||
|
||||
# Função para exibir ajuda
|
||||
mostrar_ajuda() {
|
||||
cat << EOF
|
||||
Uso: $0 [OPÇÕES]
|
||||
|
||||
Script de configuração interativa do Jitsi Meet para SGSE.
|
||||
|
||||
OPÇÕES:
|
||||
--ambiente NOME Especificar ambiente (desenvolvimento, staging, producao)
|
||||
--domain DOMINIO Configurar domínio do servidor Jitsi
|
||||
--app-id ID Configurar App ID
|
||||
--jwt-secret SECRET Configurar JWT Secret (ou gerar automaticamente se não fornecido)
|
||||
--room-prefix PREFIXO Configurar prefixo de sala
|
||||
--use-https Usar HTTPS
|
||||
--accept-self-signed Aceitar certificados autoassinados
|
||||
--test-only Apenas testar configuração existente
|
||||
--validate-only Apenas validar sem salvar
|
||||
--non-interactive Modo não-interativo (para CI/CD)
|
||||
--help Mostrar esta ajuda
|
||||
|
||||
EXEMPLOS:
|
||||
# Modo interativo
|
||||
$0
|
||||
|
||||
# Modo não-interativo
|
||||
$0 --domain meet.example.com --app-id sgse-app --use-https
|
||||
|
||||
# Apenas validar
|
||||
$0 --validate-only --domain meet.example.com
|
||||
|
||||
# Apenas testar
|
||||
$0 --test-only
|
||||
EOF
|
||||
}
|
||||
|
||||
# Processar argumentos da linha de comando
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--ambiente)
|
||||
AMBIENTE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--domain|--domínio)
|
||||
DOMAIN="$2"
|
||||
shift 2
|
||||
;;
|
||||
--app-id)
|
||||
APP_ID="$2"
|
||||
shift 2
|
||||
;;
|
||||
--jwt-secret)
|
||||
JWT_SECRET="$2"
|
||||
shift 2
|
||||
;;
|
||||
--room-prefix)
|
||||
ROOM_PREFIX="$2"
|
||||
shift 2
|
||||
;;
|
||||
--use-https)
|
||||
USE_HTTPS=true
|
||||
shift
|
||||
;;
|
||||
--accept-self-signed)
|
||||
ACCEPT_SELF_SIGNED=true
|
||||
shift
|
||||
;;
|
||||
--test-only)
|
||||
TEST_ONLY=true
|
||||
shift
|
||||
;;
|
||||
--validate-only)
|
||||
VALIDATE_ONLY=true
|
||||
shift
|
||||
;;
|
||||
--non-interactive)
|
||||
NON_INTERACTIVE=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
mostrar_ajuda
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Opção desconhecida: $1"
|
||||
mostrar_ajuda
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Banner
|
||||
echo -e "${BLUE}"
|
||||
cat << "EOF"
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ Configuração do Jitsi Meet para SGSE ║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
EOF
|
||||
echo -e "${NC}"
|
||||
|
||||
# Modo apenas teste
|
||||
if [[ "$TEST_ONLY" == "true" ]]; then
|
||||
print_info "Modo de teste apenas. Validando configuração existente..."
|
||||
# Aqui você pode adicionar lógica para testar configuração existente
|
||||
# Por exemplo, buscar do banco de dados ou arquivo de configuração
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Coletar informações
|
||||
if [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
print_info "Coletando informações de configuração..."
|
||||
echo
|
||||
fi
|
||||
|
||||
# Ambiente
|
||||
if [[ -z "$AMBIENTE" ]]; then
|
||||
ler_entrada "Ambiente (desenvolvimento, staging, producao)" "" AMBIENTE
|
||||
fi
|
||||
|
||||
# Domínio
|
||||
while [[ -z "$DOMAIN" ]] || ! validar_dominio "$DOMAIN"; do
|
||||
if [[ -z "$DOMAIN" ]]; then
|
||||
ler_entrada "Domínio do servidor Jitsi (ex: localhost:8443 ou meet.example.com)" "" DOMAIN
|
||||
else
|
||||
print_error "Domínio inválido: $DOMAIN"
|
||||
DOMAIN=""
|
||||
ler_entrada "Domínio do servidor Jitsi" "" DOMAIN
|
||||
fi
|
||||
done
|
||||
|
||||
# Detectar HTTPS automaticamente
|
||||
if [[ "$DOMAIN" =~ :8443$ ]] || [[ "$DOMAIN" =~ ^[^:]+$ ]] && [[ ! "$DOMAIN" =~ ^localhost ]]; then
|
||||
if [[ "$USE_HTTPS" != "true" ]] && [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
read -p "$(echo -e ${BLUE}Usar HTTPS? (S/n)${NC}): " resposta
|
||||
if [[ "$resposta" =~ ^[Ss]$ ]] || [[ -z "$resposta" ]]; then
|
||||
USE_HTTPS=true
|
||||
fi
|
||||
elif [[ -z "$USE_HTTPS" ]]; then
|
||||
USE_HTTPS=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Aceitar certificados autoassinados
|
||||
if [[ "$USE_HTTPS" == "true" ]] && [[ "$DOMAIN" =~ localhost ]]; then
|
||||
if [[ "$ACCEPT_SELF_SIGNED" != "true" ]] && [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
read -p "$(echo -e ${BLUE}Aceitar certificados autoassinados? (S/n)${NC}): " resposta
|
||||
if [[ "$resposta" =~ ^[Ss]$ ]] || [[ -z "$resposta" ]]; then
|
||||
ACCEPT_SELF_SIGNED=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# App ID
|
||||
while [[ -z "$APP_ID" ]] || ! validar_app_id "$APP_ID"; do
|
||||
if [[ -z "$APP_ID" ]]; then
|
||||
ler_entrada "App ID" "sgse-app" APP_ID
|
||||
else
|
||||
print_error "App ID inválido: $APP_ID (deve conter apenas letras, números, hífens e underscores)"
|
||||
APP_ID=""
|
||||
ler_entrada "App ID" "sgse-app" APP_ID
|
||||
fi
|
||||
done
|
||||
|
||||
# Room Prefix
|
||||
if [[ -z "$ROOM_PREFIX" ]]; then
|
||||
ler_entrada "Prefixo de sala" "sgse" ROOM_PREFIX
|
||||
fi
|
||||
|
||||
# JWT Secret
|
||||
if [[ -z "$JWT_SECRET" ]]; then
|
||||
if [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
read -p "$(echo -e ${BLUE}Gerar JWT Secret automaticamente? (S/n)${NC}): " resposta
|
||||
if [[ "$resposta" =~ ^[Ss]$ ]] || [[ -z "$resposta" ]]; then
|
||||
JWT_SECRET=$(gerar_jwt_secret)
|
||||
print_success "JWT Secret gerado: ${JWT_SECRET:0:16}..."
|
||||
else
|
||||
ler_entrada "JWT Secret (deixe vazio para desabilitar JWT)" "" JWT_SECRET
|
||||
fi
|
||||
else
|
||||
JWT_SECRET=$(gerar_jwt_secret)
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validar JWT Secret se fornecido
|
||||
if [[ -n "$JWT_SECRET" ]] && ! validar_jwt_secret "$JWT_SECRET"; then
|
||||
print_error "JWT Secret deve ter no mínimo 16 caracteres"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# JWT Audience (opcional)
|
||||
if [[ -z "$JWT_AUDIENCE" ]] && [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
ler_entrada "JWT Audience (opcional, padrão: domínio)" "$DOMAIN" JWT_AUDIENCE
|
||||
fi
|
||||
|
||||
# JWT Issuer (opcional)
|
||||
if [[ -z "$JWT_ISSUER" ]] && [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
ler_entrada "JWT Issuer (opcional, padrão: App ID)" "$APP_ID" JWT_ISSUER
|
||||
fi
|
||||
|
||||
# Validações
|
||||
print_info "Validando configuração..."
|
||||
|
||||
# Validar domínio
|
||||
if ! validar_dominio "$DOMAIN"; then
|
||||
print_error "Domínio inválido: $DOMAIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validar App ID
|
||||
if ! validar_app_id "$APP_ID"; then
|
||||
print_error "App ID inválido: $APP_ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validar JWT Secret
|
||||
if [[ -n "$JWT_SECRET" ]] && ! validar_jwt_secret "$JWT_SECRET"; then
|
||||
print_error "JWT Secret inválido (mínimo 16 caracteres)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "Validações básicas passaram"
|
||||
|
||||
# Testes de conectividade
|
||||
if [[ "$VALIDATE_ONLY" != "true" ]]; then
|
||||
print_info "Testando conectividade..."
|
||||
if testar_conectividade "$DOMAIN" "$USE_HTTPS"; then
|
||||
print_success "Conectividade OK"
|
||||
else
|
||||
print_warning "Não foi possível testar conectividade (servidor pode estar offline)"
|
||||
fi
|
||||
|
||||
# Validar certificado SSL se HTTPS
|
||||
if [[ "$USE_HTTPS" == "true" ]]; then
|
||||
validar_certificado_ssl "$DOMAIN"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Resumo
|
||||
echo
|
||||
print_info "Resumo da configuração:"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " Ambiente: ${AMBIENTE:-padrão}"
|
||||
echo " Domínio: $DOMAIN"
|
||||
echo " App ID: $APP_ID"
|
||||
echo " Prefixo de Sala: $ROOM_PREFIX"
|
||||
echo " HTTPS: $USE_HTTPS"
|
||||
echo " Cert. Autoassinado: $ACCEPT_SELF_SIGNED"
|
||||
if [[ -n "$JWT_SECRET" ]]; then
|
||||
echo " JWT Secret: ${JWT_SECRET:0:16}... (${#JWT_SECRET} caracteres)"
|
||||
else
|
||||
echo " JWT Secret: (não configurado)"
|
||||
fi
|
||||
if [[ -n "$JWT_AUDIENCE" ]]; then
|
||||
echo " JWT Audience: $JWT_AUDIENCE"
|
||||
fi
|
||||
if [[ -n "$JWT_ISSUER" ]]; then
|
||||
echo " JWT Issuer: $JWT_ISSUER"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Se apenas validar, sair aqui
|
||||
if [[ "$VALIDATE_ONLY" == "true" ]]; then
|
||||
print_success "Validação concluída"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Confirmar antes de salvar
|
||||
if [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
echo
|
||||
read -p "$(echo -e ${BLUE}Salvar esta configuração? (S/n)${NC}): " confirmar
|
||||
if [[ ! "$confirmar" =~ ^[Ss]$ ]] && [[ -n "$confirmar" ]]; then
|
||||
print_info "Configuração cancelada pelo usuário"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Gerar JSON de configuração
|
||||
CONFIG_JSON=$(cat <<EOF
|
||||
{
|
||||
"domain": "$DOMAIN",
|
||||
"appId": "$APP_ID",
|
||||
"roomPrefix": "$ROOM_PREFIX",
|
||||
"useHttps": $USE_HTTPS,
|
||||
"acceptSelfSignedCert": $ACCEPT_SELF_SIGNED,
|
||||
"ambiente": "${AMBIENTE:-}",
|
||||
"jwtSecret": "${JWT_SECRET:-}",
|
||||
"jwtAudience": "${JWT_AUDIENCE:-}",
|
||||
"jwtIssuer": "${JWT_ISSUER:-}"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# Salvar em arquivo
|
||||
CONFIG_FILE="jitsi-config-$(date +%Y%m%d-%H%M%S).json"
|
||||
echo "$CONFIG_JSON" > "$CONFIG_FILE"
|
||||
print_success "Configuração salva em: $CONFIG_FILE"
|
||||
|
||||
# Instruções finais
|
||||
echo
|
||||
print_info "Próximos passos:"
|
||||
echo "1. Revise o arquivo de configuração: $CONFIG_FILE"
|
||||
echo "2. Acesse o painel SGSE: TI > Configurações do Jitsi"
|
||||
echo "3. Preencha os campos com os valores acima"
|
||||
echo "4. Teste a conexão usando o botão 'Testar Conexão'"
|
||||
echo "5. Salve a configuração no painel"
|
||||
|
||||
if [[ -n "$JWT_SECRET" ]]; then
|
||||
echo
|
||||
print_warning "IMPORTANTE: Guarde o JWT Secret em local seguro!"
|
||||
print_warning "O JWT Secret não será exibido novamente."
|
||||
fi
|
||||
|
||||
print_success "Configuração concluída!"
|
||||
@@ -5,6 +5,8 @@ Simula diferentes tipos de ataques para validar o sistema de segurança
|
||||
|
||||
Autor: Sistema de Testes Automatizados
|
||||
Data: 2024
|
||||
Atualizado: 2026 - Suporte a bloqueio automático configurável
|
||||
Nota: 'engenharia_social' foi removido do sistema conforme atualização de segurança
|
||||
"""
|
||||
|
||||
import requests
|
||||
@@ -45,14 +47,14 @@ class SegurancaTeste:
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
self.resultados = {
|
||||
'brute_force': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'sql_injection': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'xss': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'ddos': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'path_traversal': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'command_injection': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'no_sql_injection': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'xxe': {'sucesso': 0, 'falhas': 0, 'detectado': False},
|
||||
'brute_force': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
'sql_injection': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
'xss': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
'ddos': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
'path_traversal': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
'command_injection': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
'nosql_injection': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
'xxe': {'sucesso': 0, 'falhas': 0, 'detectado': False, 'bloqueado_automatico': False},
|
||||
}
|
||||
|
||||
def log(self, tipo: str, mensagem: str, cor: str = Colors.OKCYAN):
|
||||
@@ -60,6 +62,25 @@ class SegurancaTeste:
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
print(f"{cor}[{timestamp}] [{tipo}] {mensagem}{Colors.ENDC}")
|
||||
|
||||
def verificar_bloqueio_automatico(self, response: requests.Response) -> bool:
|
||||
"""
|
||||
Verifica se a resposta indica bloqueio automático
|
||||
Retorna True se bloqueio automático foi aplicado
|
||||
"""
|
||||
if response.status_code == 403:
|
||||
try:
|
||||
resp_json = response.json()
|
||||
# Verificar flags de bloqueio automático
|
||||
if (resp_json.get('bloqueado') or
|
||||
resp_json.get('reason') == 'ataque_detectado' or
|
||||
resp_json.get('bloqueadoAutomatico')):
|
||||
return True
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
# Se não for JSON, verificar no texto
|
||||
if 'bloqueado automaticamente' in response.text.lower() or 'ataque_detectado' in response.text.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
def testar_brute_force(self, email: str = "test@example.com",
|
||||
tentativas: int = 10) -> bool:
|
||||
"""
|
||||
@@ -104,9 +125,16 @@ class SegurancaTeste:
|
||||
break
|
||||
|
||||
if response.status_code == 403: # Forbidden
|
||||
self.log("BRUTE_FORCE",
|
||||
f"✅ DETECTADO! Acesso negado após {i} tentativas (403)",
|
||||
Colors.OKGREEN)
|
||||
bloqueio_auto = self.verificar_bloqueio_automatico(response)
|
||||
if bloqueio_auto:
|
||||
self.log("BRUTE_FORCE",
|
||||
f"✅ BLOQUEIO AUTOMÁTICO! Ataque detectado e bloqueado após {i} tentativas (403)",
|
||||
Colors.OKGREEN)
|
||||
self.resultados['brute_force']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("BRUTE_FORCE",
|
||||
f"✅ DETECTADO! Acesso negado após {i} tentativas (403)",
|
||||
Colors.OKGREEN)
|
||||
bloqueado = True
|
||||
self.resultados['brute_force']['detectado'] = True
|
||||
break
|
||||
@@ -148,10 +176,19 @@ class SegurancaTeste:
|
||||
if r2.status_code == 200:
|
||||
jd = r2.json()
|
||||
if jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "brute_force":
|
||||
self.log("BRUTE_FORCE", "✅ DETECTADO (analisador) mesmo sem 429/403", Colors.OKGREEN)
|
||||
if jd.get("bloqueadoAutomatico"):
|
||||
self.log("BRUTE_FORCE", "✅ BLOQUEIO AUTOMÁTICO (analisador) mesmo sem 429/403", Colors.OKGREEN)
|
||||
self.resultados['brute_force']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("BRUTE_FORCE", "✅ DETECTADO (analisador) mesmo sem 429/403", Colors.OKGREEN)
|
||||
self.resultados['brute_force']['detectado'] = True
|
||||
else:
|
||||
self.log("BRUTE_FORCE", f"⚠️ AVISO: Nenhum bloqueio detectado após {tentativas} tentativas", Colors.WARNING)
|
||||
elif r2.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(r2):
|
||||
self.log("BRUTE_FORCE", "✅ BLOQUEIO AUTOMÁTICO (analisador retornou 403)", Colors.OKGREEN)
|
||||
self.resultados['brute_force']['bloqueado_automatico'] = True
|
||||
self.resultados['brute_force']['detectado'] = True
|
||||
else:
|
||||
self.log("BRUTE_FORCE", f"⚠️ AVISO: analisador retornou {r2.status_code}", Colors.WARNING)
|
||||
except Exception as e:
|
||||
@@ -162,6 +199,7 @@ class SegurancaTeste:
|
||||
def testar_sql_injection(self) -> bool:
|
||||
"""
|
||||
Testa ataques de SQL Injection em campos de entrada
|
||||
Agora verifica bloqueio automático configurável
|
||||
"""
|
||||
self.log("SQL_INJECTION", "Iniciando testes de SQL Injection...")
|
||||
|
||||
@@ -208,11 +246,18 @@ class SegurancaTeste:
|
||||
detectado = True
|
||||
self.resultados['sql_injection']['detectado'] = True
|
||||
|
||||
# Verificar se há WAF bloqueando
|
||||
# Verificar se há bloqueio automático ou WAF bloqueando
|
||||
if response.status_code == 403:
|
||||
self.log("SQL_INJECTION",
|
||||
f"✅ BLOQUEADO pelo WAF! Payload: {payload[:30]}...",
|
||||
Colors.OKGREEN)
|
||||
bloqueio_auto = self.verificar_bloqueio_automatico(response)
|
||||
if bloqueio_auto:
|
||||
self.log("SQL_INJECTION",
|
||||
f"✅ BLOQUEIO AUTOMÁTICO! Payload: {payload[:30]}...",
|
||||
Colors.OKGREEN)
|
||||
self.resultados['sql_injection']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("SQL_INJECTION",
|
||||
f"✅ BLOQUEADO pelo WAF! Payload: {payload[:30]}...",
|
||||
Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['sql_injection']['detectado'] = True
|
||||
|
||||
@@ -227,7 +272,17 @@ class SegurancaTeste:
|
||||
if r2.status_code == 200:
|
||||
jd = r2.json()
|
||||
if jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "sql_injection":
|
||||
self.log("SQL_INJECTION", f"✅ DETECTADO (analisador)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
if jd.get("bloqueadoAutomatico"):
|
||||
self.log("SQL_INJECTION", f"✅ BLOQUEIO AUTOMÁTICO (analisador)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
self.resultados['sql_injection']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("SQL_INJECTION", f"✅ DETECTADO (analisador)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['sql_injection']['detectado'] = True
|
||||
elif r2.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(r2):
|
||||
self.log("SQL_INJECTION", f"✅ BLOQUEIO AUTOMÁTICO (analisador retornou 403)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
self.resultados['sql_injection']['bloqueado_automatico'] = True
|
||||
detectado = True
|
||||
self.resultados['sql_injection']['detectado'] = True
|
||||
except Exception:
|
||||
@@ -246,6 +301,7 @@ class SegurancaTeste:
|
||||
def testar_xss(self) -> bool:
|
||||
"""
|
||||
Testa ataques de Cross-Site Scripting (XSS)
|
||||
Agora verifica bloqueio automático configurável
|
||||
"""
|
||||
self.log("XSS", "Iniciando testes de XSS...")
|
||||
|
||||
@@ -293,9 +349,16 @@ class SegurancaTeste:
|
||||
self.resultados['xss']['detectado'] = True
|
||||
|
||||
if response.status_code == 403:
|
||||
self.log("XSS",
|
||||
f"✅ BLOQUEADO! Payload: {payload[:30]}...",
|
||||
Colors.OKGREEN)
|
||||
bloqueio_auto = self.verificar_bloqueio_automatico(response)
|
||||
if bloqueio_auto:
|
||||
self.log("XSS",
|
||||
f"✅ BLOQUEIO AUTOMÁTICO! Payload: {payload[:30]}...",
|
||||
Colors.OKGREEN)
|
||||
self.resultados['xss']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("XSS",
|
||||
f"✅ BLOQUEADO! Payload: {payload[:30]}...",
|
||||
Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['xss']['detectado'] = True
|
||||
|
||||
@@ -306,7 +369,17 @@ class SegurancaTeste:
|
||||
if r2.status_code == 200:
|
||||
jd = r2.json()
|
||||
if jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "xss":
|
||||
self.log("XSS", f"✅ DETECTADO (analisador)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
if jd.get("bloqueadoAutomatico"):
|
||||
self.log("XSS", f"✅ BLOQUEIO AUTOMÁTICO (analisador)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
self.resultados['xss']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("XSS", f"✅ DETECTADO (analisador)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['xss']['detectado'] = True
|
||||
elif r2.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(r2):
|
||||
self.log("XSS", f"✅ BLOQUEIO AUTOMÁTICO (analisador retornou 403)! Payload: {payload[:30]}...", Colors.OKGREEN)
|
||||
self.resultados['xss']['bloqueado_automatico'] = True
|
||||
detectado = True
|
||||
self.resultados['xss']['detectado'] = True
|
||||
except Exception:
|
||||
@@ -335,9 +408,10 @@ class SegurancaTeste:
|
||||
inicio = time.time()
|
||||
total_requisicoes = 0
|
||||
bloqueios = 0
|
||||
bloqueios_automaticos = 0
|
||||
|
||||
def fazer_requisicao():
|
||||
nonlocal total_requisicoes, bloqueios
|
||||
nonlocal total_requisicoes, bloqueios, bloqueios_automaticos
|
||||
try:
|
||||
payload = {
|
||||
"email": f"ddos_test_{random.randint(1000, 9999)}@example.com",
|
||||
@@ -357,6 +431,12 @@ class SegurancaTeste:
|
||||
bloqueios += 1
|
||||
return True
|
||||
|
||||
if response.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(response):
|
||||
bloqueios_automaticos += 1
|
||||
bloqueios += 1
|
||||
return True
|
||||
|
||||
if response.status_code == 503: # Service Unavailable
|
||||
bloqueios += 1
|
||||
return True
|
||||
@@ -373,6 +453,8 @@ class SegurancaTeste:
|
||||
if r.status_code == 200:
|
||||
jd = r.json()
|
||||
return jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "ddos"
|
||||
elif r.status_code == 403:
|
||||
return self.verificar_bloqueio_automatico(r)
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
@@ -403,10 +485,14 @@ class SegurancaTeste:
|
||||
|
||||
taxa_bloqueio = (bloqueios / total_requisicoes * 100) if total_requisicoes > 0 else 0
|
||||
|
||||
if bloqueios_automaticos > 0:
|
||||
self.resultados['ddos']['bloqueado_automatico'] = True
|
||||
|
||||
if detectado or taxa_bloqueio > 50:
|
||||
self.log("DDoS",
|
||||
f"✅ DETECTADO! {bloqueios}/{total_requisicoes} requisições bloqueadas ({taxa_bloqueio:.1f}%)",
|
||||
Colors.OKGREEN)
|
||||
msg = f"✅ DETECTADO! {bloqueios}/{total_requisicoes} requisições bloqueadas ({taxa_bloqueio:.1f}%)"
|
||||
if bloqueios_automaticos > 0:
|
||||
msg += f" | {bloqueios_automaticos} bloqueios automáticos"
|
||||
self.log("DDoS", msg, Colors.OKGREEN)
|
||||
self.resultados['ddos']['detectado'] = True
|
||||
else:
|
||||
self.log("DDoS",
|
||||
@@ -418,6 +504,7 @@ class SegurancaTeste:
|
||||
def testar_path_traversal(self) -> bool:
|
||||
"""
|
||||
Testa ataques de Path Traversal
|
||||
Agora verifica bloqueio automático configurável
|
||||
"""
|
||||
self.log("PATH_TRAVERSAL", "Iniciando testes de Path Traversal...")
|
||||
|
||||
@@ -448,9 +535,16 @@ class SegurancaTeste:
|
||||
)
|
||||
|
||||
if response.status_code == 403:
|
||||
self.log("PATH_TRAVERSAL",
|
||||
f"✅ BLOQUEADO! Payload: {payload}",
|
||||
Colors.OKGREEN)
|
||||
bloqueio_auto = self.verificar_bloqueio_automatico(response)
|
||||
if bloqueio_auto:
|
||||
self.log("PATH_TRAVERSAL",
|
||||
f"✅ BLOQUEIO AUTOMÁTICO! Payload: {payload}",
|
||||
Colors.OKGREEN)
|
||||
self.resultados['path_traversal']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("PATH_TRAVERSAL",
|
||||
f"✅ BLOQUEADO! Payload: {payload}",
|
||||
Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['path_traversal']['detectado'] = True
|
||||
|
||||
@@ -461,7 +555,17 @@ class SegurancaTeste:
|
||||
if r2.status_code == 200:
|
||||
jd = r2.json()
|
||||
if jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "path_traversal":
|
||||
self.log("PATH_TRAVERSAL", f"✅ DETECTADO (analisador)! Payload: {payload}", Colors.OKGREEN)
|
||||
if jd.get("bloqueadoAutomatico"):
|
||||
self.log("PATH_TRAVERSAL", f"✅ BLOQUEIO AUTOMÁTICO (analisador)! Payload: {payload}", Colors.OKGREEN)
|
||||
self.resultados['path_traversal']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("PATH_TRAVERSAL", f"✅ DETECTADO (analisador)! Payload: {payload}", Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['path_traversal']['detectado'] = True
|
||||
elif r2.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(r2):
|
||||
self.log("PATH_TRAVERSAL", f"✅ BLOQUEIO AUTOMÁTICO (analisador retornou 403)! Payload: {payload}", Colors.OKGREEN)
|
||||
self.resultados['path_traversal']['bloqueado_automatico'] = True
|
||||
detectado = True
|
||||
self.resultados['path_traversal']['detectado'] = True
|
||||
except Exception:
|
||||
@@ -475,6 +579,7 @@ class SegurancaTeste:
|
||||
def testar_command_injection(self) -> bool:
|
||||
"""
|
||||
Testa ataques de Command Injection
|
||||
Agora verifica bloqueio automático configurável
|
||||
"""
|
||||
self.log("COMMAND_INJECTION", "Iniciando testes de Command Injection...")
|
||||
|
||||
@@ -511,9 +616,16 @@ class SegurancaTeste:
|
||||
)
|
||||
|
||||
if response.status_code == 403:
|
||||
self.log("COMMAND_INJECTION",
|
||||
f"✅ BLOQUEADO! Payload: {payload}",
|
||||
Colors.OKGREEN)
|
||||
bloqueio_auto = self.verificar_bloqueio_automatico(response)
|
||||
if bloqueio_auto:
|
||||
self.log("COMMAND_INJECTION",
|
||||
f"✅ BLOQUEIO AUTOMÁTICO! Payload: {payload}",
|
||||
Colors.OKGREEN)
|
||||
self.resultados['command_injection']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("COMMAND_INJECTION",
|
||||
f"✅ BLOQUEADO! Payload: {payload}",
|
||||
Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['command_injection']['detectado'] = True
|
||||
|
||||
@@ -524,7 +636,17 @@ class SegurancaTeste:
|
||||
if r2.status_code == 200:
|
||||
jd = r2.json()
|
||||
if jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "command_injection":
|
||||
self.log("COMMAND_INJECTION", f"✅ DETECTADO (analisador)! Payload: {payload}", Colors.OKGREEN)
|
||||
if jd.get("bloqueadoAutomatico"):
|
||||
self.log("COMMAND_INJECTION", f"✅ BLOQUEIO AUTOMÁTICO (analisador)! Payload: {payload}", Colors.OKGREEN)
|
||||
self.resultados['command_injection']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("COMMAND_INJECTION", f"✅ DETECTADO (analisador)! Payload: {payload}", Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['command_injection']['detectado'] = True
|
||||
elif r2.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(r2):
|
||||
self.log("COMMAND_INJECTION", f"✅ BLOQUEIO AUTOMÁTICO (analisador retornou 403)! Payload: {payload}", Colors.OKGREEN)
|
||||
self.resultados['command_injection']['bloqueado_automatico'] = True
|
||||
detectado = True
|
||||
self.resultados['command_injection']['detectado'] = True
|
||||
except Exception:
|
||||
@@ -538,6 +660,7 @@ class SegurancaTeste:
|
||||
def testar_no_sql_injection(self) -> bool:
|
||||
"""
|
||||
Testa ataques de NoSQL Injection (para sistemas que usam MongoDB, etc)
|
||||
Agora verifica bloqueio automático configurável
|
||||
"""
|
||||
self.log("NOSQL_INJECTION", "Iniciando testes de NoSQL Injection...")
|
||||
|
||||
@@ -552,27 +675,63 @@ class SegurancaTeste:
|
||||
]
|
||||
|
||||
endpoint = f"{self.base_url}/api/auth/sign-in/email"
|
||||
endpoint_analyze = urljoin(self.convex_url if self.convex_url.endswith('/') else self.convex_url + '/', "http/security/analyze?dst=127.0.0.1&proto=http")
|
||||
detectado = False
|
||||
|
||||
for payload in payloads_nosql:
|
||||
try:
|
||||
ip_origem = f"203.0.113.{random.randint(10, 250)}"
|
||||
# Tentar enviar como JSON malicioso
|
||||
response = self.session.post(
|
||||
endpoint,
|
||||
json={"email": payload, "password": {"$ne": None}},
|
||||
headers={"X-Forwarded-For": f"203.0.113.{random.randint(10, 250)}"},
|
||||
headers={"X-Forwarded-For": ip_origem},
|
||||
timeout=5,
|
||||
allow_redirects=False
|
||||
)
|
||||
|
||||
if response.status_code in [400, 403, 422]:
|
||||
self.log("NOSQL_INJECTION",
|
||||
f"✅ DETECTADO/BLOQUEADO! Payload: {str(payload)[:50]}...",
|
||||
Colors.OKGREEN)
|
||||
if response.status_code == 403:
|
||||
bloqueio_auto = self.verificar_bloqueio_automatico(response)
|
||||
if bloqueio_auto:
|
||||
self.log("NOSQL_INJECTION",
|
||||
f"✅ BLOQUEIO AUTOMÁTICO! Payload: {str(payload)[:50]}...",
|
||||
Colors.OKGREEN)
|
||||
self.resultados['nosql_injection']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("NOSQL_INJECTION",
|
||||
f"✅ DETECTADO/BLOQUEADO! Payload: {str(payload)[:50]}...",
|
||||
Colors.OKGREEN)
|
||||
else:
|
||||
self.log("NOSQL_INJECTION",
|
||||
f"✅ DETECTADO/BLOQUEADO! Payload: {str(payload)[:50]}...",
|
||||
Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['no_sql_injection']['detectado'] = True
|
||||
self.resultados['nosql_injection']['detectado'] = True
|
||||
|
||||
time.sleep(0.3)
|
||||
# Registrar via analisador HTTP
|
||||
try:
|
||||
payload_str = json.dumps(payload)
|
||||
r2 = self.session.post(endpoint_analyze, data=payload_str, headers={"Content-Type":"application/json","X-Forwarded-For": ip_origem})
|
||||
if r2.status_code == 200:
|
||||
jd = r2.json()
|
||||
if jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "nosql_injection":
|
||||
if jd.get("bloqueadoAutomatico"):
|
||||
self.log("NOSQL_INJECTION", f"✅ BLOQUEIO AUTOMÁTICO (analisador)! Payload: {str(payload)[:50]}...", Colors.OKGREEN)
|
||||
self.resultados['nosql_injection']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("NOSQL_INJECTION", f"✅ DETECTADO (analisador)! Payload: {str(payload)[:50]}...", Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['nosql_injection']['detectado'] = True
|
||||
elif r2.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(r2):
|
||||
self.log("NOSQL_INJECTION", f"✅ BLOQUEIO AUTOMÁTICO (analisador retornou 403)! Payload: {str(payload)[:50]}...", Colors.OKGREEN)
|
||||
self.resultados['nosql_injection']['bloqueado_automatico'] = True
|
||||
detectado = True
|
||||
self.resultados['nosql_injection']['detectado'] = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.log("NOSQL_INJECTION", f"Erro: {str(e)}", Colors.WARNING)
|
||||
@@ -582,6 +741,7 @@ class SegurancaTeste:
|
||||
def testar_xxe(self) -> bool:
|
||||
"""
|
||||
Testa ataques de XXE (XML External Entity)
|
||||
Agora verifica bloqueio automático configurável
|
||||
"""
|
||||
self.log("XXE", "Iniciando testes de XXE...")
|
||||
|
||||
@@ -608,9 +768,21 @@ class SegurancaTeste:
|
||||
)
|
||||
|
||||
if response.status_code in [400, 403, 415]:
|
||||
self.log("XXE",
|
||||
f"✅ BLOQUEADO! Tipo de conteúdo XML rejeitado",
|
||||
Colors.OKGREEN)
|
||||
if response.status_code == 403:
|
||||
bloqueio_auto = self.verificar_bloqueio_automatico(response)
|
||||
if bloqueio_auto:
|
||||
self.log("XXE",
|
||||
f"✅ BLOQUEIO AUTOMÁTICO! Tipo de conteúdo XML bloqueado",
|
||||
Colors.OKGREEN)
|
||||
self.resultados['xxe']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("XXE",
|
||||
f"✅ BLOQUEADO! Tipo de conteúdo XML rejeitado",
|
||||
Colors.OKGREEN)
|
||||
else:
|
||||
self.log("XXE",
|
||||
f"✅ BLOQUEADO! Tipo de conteúdo XML rejeitado",
|
||||
Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['xxe']['detectado'] = True
|
||||
|
||||
@@ -622,7 +794,17 @@ class SegurancaTeste:
|
||||
if r2.status_code == 200:
|
||||
jd = r2.json()
|
||||
if jd.get("ataqueDetectado") and jd.get("tipoAtaque") == "xxe":
|
||||
self.log("XXE", "✅ DETECTADO (analisador)!", Colors.OKGREEN)
|
||||
if jd.get("bloqueadoAutomatico"):
|
||||
self.log("XXE", "✅ BLOQUEIO AUTOMÁTICO (analisador)!", Colors.OKGREEN)
|
||||
self.resultados['xxe']['bloqueado_automatico'] = True
|
||||
else:
|
||||
self.log("XXE", "✅ DETECTADO (analisador)!", Colors.OKGREEN)
|
||||
detectado = True
|
||||
self.resultados['xxe']['detectado'] = True
|
||||
elif r2.status_code == 403:
|
||||
if self.verificar_bloqueio_automatico(r2):
|
||||
self.log("XXE", "✅ BLOQUEIO AUTOMÁTICO (analisador retornou 403)!", Colors.OKGREEN)
|
||||
self.resultados['xxe']['bloqueado_automatico'] = True
|
||||
detectado = True
|
||||
self.resultados['xxe']['detectado'] = True
|
||||
except Exception:
|
||||
@@ -662,13 +844,18 @@ class SegurancaTeste:
|
||||
|
||||
total_testes = len(self.resultados)
|
||||
total_detectados = sum(1 for r in self.resultados.values() if r['detectado'])
|
||||
total_bloqueios_automaticos = sum(1 for r in self.resultados.values() if r.get('bloqueado_automatico', False))
|
||||
|
||||
for tipo, resultado in self.resultados.items():
|
||||
status = "✅ DETECTADO" if resultado['detectado'] else "❌ NÃO DETECTADO"
|
||||
cor = Colors.OKGREEN if resultado['detectado'] else Colors.FAIL
|
||||
|
||||
bloqueio_auto = ""
|
||||
if resultado.get('bloqueado_automatico'):
|
||||
bloqueio_auto = f" {Colors.BOLD}🔒 BLOQUEIO AUTOMÁTICO ATIVO{Colors.ENDC}"
|
||||
|
||||
print(f"{cor}[{tipo.upper().replace('_', ' ')}]{Colors.ENDC}")
|
||||
print(f" Status: {cor}{status}{Colors.ENDC}")
|
||||
print(f" Status: {cor}{status}{Colors.ENDC}{bloqueio_auto}")
|
||||
print(f" Sucessos: {resultado['sucesso']}")
|
||||
print(f" Falhas: {resultado['falhas']}")
|
||||
print()
|
||||
@@ -677,6 +864,8 @@ class SegurancaTeste:
|
||||
print(f"Total de Testes: {total_testes}")
|
||||
print(f"{Colors.OKGREEN if total_detectados == total_testes else Colors.WARNING}")
|
||||
print(f"Ataques Detectados: {total_detectados}/{total_testes}")
|
||||
if total_bloqueios_automaticos > 0:
|
||||
print(f"{Colors.BOLD}🔒 Bloqueios Automáticos: {total_bloqueios_automaticos}/{total_testes}{Colors.ENDC}")
|
||||
print(f"{Colors.ENDC}")
|
||||
|
||||
# Recomendações
|
||||
@@ -686,18 +875,29 @@ class SegurancaTeste:
|
||||
for tipo in tipos_nao_detectados:
|
||||
print(f" • Revisar proteção contra {tipo.replace('_', ' ')}")
|
||||
|
||||
print(f"\n{Colors.BOLD}{'='*70}{Colors.ENDC}\n")
|
||||
if total_bloqueios_automaticos < total_detectados:
|
||||
tipos_sem_auto = [tipo for tipo, r in self.resultados.items()
|
||||
if r['detectado'] and not r.get('bloqueado_automatico', False)]
|
||||
if tipos_sem_auto:
|
||||
print(f"\n{Colors.WARNING}{Colors.BOLD}SUGESTÃO:{Colors.ENDC}")
|
||||
print(f" • Considerar configurar bloqueio automático para: {', '.join(tipos_sem_auto)}")
|
||||
|
||||
print(f"\n{Colors.BOLD}{'='*70}{Colors.ENDC}")
|
||||
print(f"{Colors.OKCYAN}Nota: 'engenharia_social' foi removido do sistema conforme atualização de segurança.{Colors.ENDC}")
|
||||
print(f"{Colors.BOLD}{'='*70}{Colors.ENDC}\n")
|
||||
|
||||
def executar_todos_testes(self):
|
||||
"""Executa todos os testes de segurança"""
|
||||
print(f"\n{Colors.BOLD}{Colors.HEADER}")
|
||||
print("╔" + "═" * 68 + "╗")
|
||||
print("║" + " " * 10 + "TESTES DE SEGURANÇA - SGSE" + " " * 30 + "║")
|
||||
print("║" + " " * 5 + "Atualizado: Suporte a Bloqueio Automático" + " " * 20 + "║")
|
||||
print("╚" + "═" * 68 + "╝")
|
||||
print(f"{Colors.ENDC}\n")
|
||||
|
||||
self.log("INICIO", f"URL Base: {self.base_url}")
|
||||
self.log("INICIO", f"URL Convex: {self.convex_url}\n")
|
||||
self.log("INICIO", f"URL Convex: {self.convex_url}")
|
||||
self.log("INICIO", "Verificando bloqueio automático configurável...\n")
|
||||
|
||||
# Executar testes sequenciais
|
||||
testes = [
|
||||
@@ -728,7 +928,7 @@ def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Teste de Segurança para SGSE',
|
||||
description='Teste de Segurança para SGSE - Suporta bloqueio automático configurável',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Exemplos:
|
||||
@@ -743,6 +943,9 @@ Exemplos:
|
||||
|
||||
# Teste DDoS com mais threads
|
||||
python teste_seguranca.py --teste ddos --ddos-threads 100
|
||||
|
||||
Nota: O script agora verifica bloqueio automático configurável.
|
||||
'engenharia_social' foi removido do sistema.
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -758,7 +961,7 @@ Exemplos:
|
||||
choices=['brute_force', 'sql_injection', 'xss', 'ddos',
|
||||
'path_traversal', 'command_injection', 'nosql', 'xxe', 'todos'],
|
||||
default='todos',
|
||||
help='Tipo de teste a executar (padrão: todos)')
|
||||
help='Tipo de teste a executar (padrão: todos). Nota: "engenharia_social" foi removido do sistema.')
|
||||
|
||||
parser.add_argument('--brute-force-tentativas',
|
||||
type=int,
|
||||
@@ -810,7 +1013,3 @@ Exemplos:
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user