GuardianUnivalle-Benito-Yucra 0.1.45__tar.gz → 0.1.47__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of GuardianUnivalle-Benito-Yucra might be problematic. Click here for more details.
- guardianunivalle_benito_yucra-0.1.47/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py +118 -0
- guardianunivalle_benito_yucra-0.1.47/GuardianUnivalle_Benito_Yucra/auditoria/utils_auditoria.py +57 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py +78 -37
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/PKG-INFO +1 -1
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/SOURCES.txt +1 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/PKG-INFO +1 -1
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/pyproject.toml +1 -1
- guardianunivalle_benito_yucra-0.1.45/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py +0 -40
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/__init__.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/criptografia/kdf.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/mitigacion/limitador_peticion.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/mitigacion/lista_bloqueo.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/puntuacion/puntuacion_amenaza.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/utilidades.py +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/dependency_links.txt +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/requires.txt +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/top_level.txt +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/LICENSE +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/README.md +0 -0
- {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/setup.cfg +0 -0
guardianunivalle_benito_yucra-0.1.47/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import datetime
|
|
3
|
+
import json
|
|
4
|
+
import platform
|
|
5
|
+
from django.utils import timezone
|
|
6
|
+
|
|
7
|
+
LOG_FILE = "auditoria_guardian.log"
|
|
8
|
+
|
|
9
|
+
# =====================================================
|
|
10
|
+
# === FUNCIONES DE CAPTURA Y ANÁLISIS DE CLIENTE ===
|
|
11
|
+
# =====================================================
|
|
12
|
+
def capturar_datos_cliente(request) -> dict:
|
|
13
|
+
"""Extrae información útil del cliente que accede al sistema."""
|
|
14
|
+
ip = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
15
|
+
if ip:
|
|
16
|
+
ip = ip.split(",")[0].strip()
|
|
17
|
+
else:
|
|
18
|
+
ip = request.META.get("REMOTE_ADDR", "Desconocida")
|
|
19
|
+
|
|
20
|
+
user_agent = request.META.get("HTTP_USER_AGENT", "Desconocido")
|
|
21
|
+
metodo = request.method
|
|
22
|
+
ruta = request.path
|
|
23
|
+
parametros = request.GET.dict() if metodo == "GET" else request.POST.dict()
|
|
24
|
+
so_servidor = platform.system()
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
"ip_cliente": ip,
|
|
28
|
+
"navegador": user_agent,
|
|
29
|
+
"metodo": metodo,
|
|
30
|
+
"ruta": ruta,
|
|
31
|
+
"parametros": parametros,
|
|
32
|
+
"servidor_os": so_servidor,
|
|
33
|
+
"hora_servidor": timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def analizar_comportamiento_cliente(datos_cliente: dict) -> tuple[str, str]:
|
|
38
|
+
"""
|
|
39
|
+
Aplica reglas básicas de detección:
|
|
40
|
+
- IP sospechosa o repetitiva
|
|
41
|
+
- Agente extraño o vacío
|
|
42
|
+
- Peticiones sospechosas (ej: /admin, /etc/passwd)
|
|
43
|
+
Devuelve: (nivel_riesgo, descripcion)
|
|
44
|
+
"""
|
|
45
|
+
descripcion = []
|
|
46
|
+
riesgo = "BAJO"
|
|
47
|
+
|
|
48
|
+
ip = datos_cliente.get("ip_cliente", "")
|
|
49
|
+
user_agent = datos_cliente.get("navegador", "").lower()
|
|
50
|
+
ruta = datos_cliente.get("ruta", "")
|
|
51
|
+
|
|
52
|
+
# === Reglas simples ===
|
|
53
|
+
if not user_agent or "curl" in user_agent or "python" in user_agent:
|
|
54
|
+
descripcion.append("Agente de usuario anómalo (posible bot o script).")
|
|
55
|
+
riesgo = "MEDIO"
|
|
56
|
+
|
|
57
|
+
if "admin" in ruta or "etc/passwd" in ruta or "../" in ruta:
|
|
58
|
+
descripcion.append("Ruta sospechosa accedida.")
|
|
59
|
+
riesgo = "ALTO"
|
|
60
|
+
|
|
61
|
+
if "Desconocida" in ip or ip.startswith("192.168.") is False:
|
|
62
|
+
descripcion.append(f"IP externa detectada: {ip}")
|
|
63
|
+
riesgo = "MEDIO"
|
|
64
|
+
|
|
65
|
+
# Comportamiento sin parámetros ni cabeceras
|
|
66
|
+
if not datos_cliente.get("parametros"):
|
|
67
|
+
descripcion.append("Petición sin parámetros ni cabeceras útiles.")
|
|
68
|
+
riesgo = "BAJO"
|
|
69
|
+
|
|
70
|
+
if not descripcion:
|
|
71
|
+
descripcion.append("Acceso normal detectado.")
|
|
72
|
+
|
|
73
|
+
return riesgo, " | ".join(descripcion)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# =====================================================
|
|
77
|
+
# === FUNCIÓN PRINCIPAL DE REGISTRO ===
|
|
78
|
+
# =====================================================
|
|
79
|
+
def registrar_evento(request, tipo: str = "ACCESO", extra: dict | None = None):
|
|
80
|
+
"""Registra un evento de auditoría detallado del cliente."""
|
|
81
|
+
try:
|
|
82
|
+
datos_cliente = capturar_datos_cliente(request)
|
|
83
|
+
severidad, descripcion = analizar_comportamiento_cliente(datos_cliente)
|
|
84
|
+
|
|
85
|
+
evento = {
|
|
86
|
+
"fecha": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
87
|
+
"tipo": tipo,
|
|
88
|
+
"descripcion": descripcion,
|
|
89
|
+
"severidad": severidad,
|
|
90
|
+
"cliente": datos_cliente,
|
|
91
|
+
"extra": extra or {},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Crear carpeta solo si hay directorio en la ruta
|
|
95
|
+
log_dir = os.path.dirname(LOG_FILE)
|
|
96
|
+
if log_dir:
|
|
97
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
98
|
+
|
|
99
|
+
# Registrar en archivo
|
|
100
|
+
with open(LOG_FILE, "a", encoding="utf-8") as f:
|
|
101
|
+
f.write(json.dumps(evento, ensure_ascii=False) + "\n")
|
|
102
|
+
|
|
103
|
+
# (Opcional) Log en consola
|
|
104
|
+
print(f"[AUDITORÍA] Evento registrado: {evento['descripcion']} (nivel {severidad})")
|
|
105
|
+
|
|
106
|
+
except Exception as e:
|
|
107
|
+
print(f"[Auditoría] Error al registrar evento: {e}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# =====================================================
|
|
111
|
+
# === CONSULTA DE REGISTROS ===
|
|
112
|
+
# =====================================================
|
|
113
|
+
def generar_reporte() -> str:
|
|
114
|
+
"""Devuelve todo el contenido del archivo de auditoría."""
|
|
115
|
+
if not os.path.exists(LOG_FILE):
|
|
116
|
+
return "No hay registros aún."
|
|
117
|
+
with open(LOG_FILE, "r", encoding="utf-8") as f:
|
|
118
|
+
return f.read()
|
guardianunivalle_benito_yucra-0.1.47/GuardianUnivalle_Benito_Yucra/auditoria/utils_auditoria.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from django.utils.timezone import now
|
|
3
|
+
from user_agents import parse
|
|
4
|
+
|
|
5
|
+
def obtener_datos_maquina(request):
|
|
6
|
+
"""Obtiene información detallada del cliente desde la petición"""
|
|
7
|
+
try:
|
|
8
|
+
# --- IP real ---
|
|
9
|
+
ip = (
|
|
10
|
+
request.META.get("HTTP_X_FORWARDED_FOR")
|
|
11
|
+
or request.META.get("REMOTE_ADDR")
|
|
12
|
+
or "0.0.0.0"
|
|
13
|
+
)
|
|
14
|
+
ip = ip.split(",")[0].strip()
|
|
15
|
+
|
|
16
|
+
# --- User Agent ---
|
|
17
|
+
user_agent_str = request.META.get("HTTP_USER_AGENT", "Desconocido")
|
|
18
|
+
user_agent = parse(user_agent_str)
|
|
19
|
+
navegador = f"{user_agent.browser.family} {user_agent.browser.version_string}"
|
|
20
|
+
sistema = f"{user_agent.os.family} {user_agent.os.version_string}"
|
|
21
|
+
|
|
22
|
+
# --- Geolocalización (usando ipinfo.io gratuita) ---
|
|
23
|
+
geo_data = {}
|
|
24
|
+
try:
|
|
25
|
+
r = requests.get(f"https://ipinfo.io/{ip}/json", timeout=2)
|
|
26
|
+
if r.status_code == 200:
|
|
27
|
+
geo_data = r.json()
|
|
28
|
+
except Exception:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
pais = geo_data.get("country", "Desconocido")
|
|
32
|
+
ciudad = geo_data.get("city", "Desconocida")
|
|
33
|
+
isp = geo_data.get("org", "Desconocido")
|
|
34
|
+
|
|
35
|
+
# --- Usuario autenticado ---
|
|
36
|
+
usuario = "Anónimo"
|
|
37
|
+
if request.user and request.user.is_authenticated:
|
|
38
|
+
usuario = request.user.username
|
|
39
|
+
|
|
40
|
+
# --- Construir estructura ---
|
|
41
|
+
datos = {
|
|
42
|
+
"fecha": now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
43
|
+
"ip": ip,
|
|
44
|
+
"pais": pais,
|
|
45
|
+
"ciudad": ciudad,
|
|
46
|
+
"isp": isp,
|
|
47
|
+
"usuario": usuario,
|
|
48
|
+
"user_agent": user_agent_str,
|
|
49
|
+
"navegador": navegador,
|
|
50
|
+
"sistema_operativo": sistema,
|
|
51
|
+
"url": request.path,
|
|
52
|
+
"metodo": request.method,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return datos
|
|
56
|
+
except Exception as e:
|
|
57
|
+
return {"error": str(e)}
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
import time
|
|
3
3
|
import logging
|
|
4
4
|
import json
|
|
5
|
+
from collections import deque
|
|
5
6
|
from typing import Dict, List
|
|
6
7
|
from django.conf import settings
|
|
7
8
|
from django.utils.deprecation import MiddlewareMixin
|
|
@@ -9,7 +10,7 @@ from ..mitigacion.limitador_peticion import limitar_peticion
|
|
|
9
10
|
from ..auditoria.registro_auditoria import registrar_evento
|
|
10
11
|
|
|
11
12
|
# =====================================================
|
|
12
|
-
# ===
|
|
13
|
+
# === CONFIGURACIÓN DEL LOGGER ===
|
|
13
14
|
# =====================================================
|
|
14
15
|
logger = logging.getLogger("dosdefense")
|
|
15
16
|
logger.setLevel(logging.INFO)
|
|
@@ -19,108 +20,148 @@ if not logger.handlers:
|
|
|
19
20
|
logger.addHandler(handler)
|
|
20
21
|
|
|
21
22
|
# =====================================================
|
|
22
|
-
# ===
|
|
23
|
+
# === PARÁMETROS DE CONFIGURACIÓN BASE ===
|
|
23
24
|
# =====================================================
|
|
24
25
|
LIMITE_PETICIONES = getattr(settings, "DOS_LIMITE_PETICIONES", 100) # por minuto
|
|
25
26
|
VENTANA_SEGUNDOS = getattr(settings, "DOS_VENTANA_SEGUNDOS", 60)
|
|
26
27
|
PESO_DOS = getattr(settings, "DOS_PESO", 0.6)
|
|
28
|
+
LIMITE_ENDPOINTS_DISTINTOS = getattr(settings, "DOS_LIMITE_ENDPOINTS", 50)
|
|
29
|
+
TRUSTED_IPS = getattr(settings, "DOS_TRUSTED_IPS", [])
|
|
27
30
|
|
|
28
31
|
# =====================================================
|
|
29
|
-
# ===
|
|
32
|
+
# === REGISTRO TEMPORAL EN MEMORIA ===
|
|
30
33
|
# =====================================================
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
# Estructura: { ip: deque([timestamps]), ... }
|
|
35
|
+
# deque es eficiente para ventanas deslizantes
|
|
36
|
+
REGISTRO_SOLICITUDES: Dict[str, deque] = {}
|
|
37
|
+
REGISTRO_ENDPOINTS: Dict[str, set] = {}
|
|
34
38
|
|
|
35
39
|
# =====================================================
|
|
36
|
-
# ===
|
|
40
|
+
# === FUNCIONES AUXILIARES ===
|
|
37
41
|
# =====================================================
|
|
38
42
|
def get_client_ip(request) -> str:
|
|
39
43
|
"""Obtiene la IP real del cliente (considera proxies)."""
|
|
40
44
|
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
41
45
|
if x_forwarded_for:
|
|
42
46
|
return x_forwarded_for.split(",")[0].strip()
|
|
43
|
-
return request.META.get("REMOTE_ADDR", "")
|
|
47
|
+
return request.META.get("REMOTE_ADDR", "") or "0.0.0.0"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def limpiar_registro_global():
|
|
51
|
+
"""Elimina IPs sin actividad reciente para evitar uso excesivo de memoria."""
|
|
52
|
+
ahora = time.time()
|
|
53
|
+
expiracion = VENTANA_SEGUNDOS * 2 # doble ventana
|
|
54
|
+
inactivas = [
|
|
55
|
+
ip for ip, tiempos in REGISTRO_SOLICITUDES.items()
|
|
56
|
+
if tiempos and ahora - tiempos[-1] > expiracion
|
|
57
|
+
]
|
|
58
|
+
for ip in inactivas:
|
|
59
|
+
REGISTRO_SOLICITUDES.pop(ip, None)
|
|
60
|
+
REGISTRO_ENDPOINTS.pop(ip, None)
|
|
44
61
|
|
|
45
62
|
|
|
46
63
|
def limpiar_registro(ip: str):
|
|
47
64
|
"""Limpia peticiones antiguas fuera de la ventana de tiempo."""
|
|
48
65
|
ahora = time.time()
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
]
|
|
66
|
+
if ip not in REGISTRO_SOLICITUDES:
|
|
67
|
+
REGISTRO_SOLICITUDES[ip] = deque()
|
|
68
|
+
tiempos = REGISTRO_SOLICITUDES[ip]
|
|
69
|
+
while tiempos and ahora - tiempos[0] > VENTANA_SEGUNDOS:
|
|
70
|
+
tiempos.popleft()
|
|
52
71
|
|
|
53
72
|
|
|
54
|
-
def calcular_nivel_amenaza_dos(
|
|
55
|
-
tasa_peticion: int, limite: int = LIMITE_PETICIONES
|
|
56
|
-
) -> float:
|
|
73
|
+
def calcular_nivel_amenaza_dos(tasa_peticion: int, limite: int = LIMITE_PETICIONES) -> float:
|
|
57
74
|
"""
|
|
58
|
-
Calcula la puntuación de amenaza DoS
|
|
75
|
+
Calcula la puntuación de amenaza DoS.
|
|
59
76
|
Fórmula: S_dos = w_dos * (tasa_peticion / limite)
|
|
60
77
|
"""
|
|
61
|
-
proporcion = tasa_peticion / limite
|
|
62
|
-
s_dos = PESO_DOS * proporcion
|
|
78
|
+
proporcion = tasa_peticion / max(limite, 1)
|
|
79
|
+
s_dos = PESO_DOS * min(proporcion, 2.0)
|
|
63
80
|
return round(min(s_dos, 1.0), 3)
|
|
64
81
|
|
|
65
82
|
|
|
66
83
|
def detectar_dos(ip: str, tasa_peticion: int, limite: int = LIMITE_PETICIONES) -> bool:
|
|
67
84
|
"""Evalúa si la tasa de peticiones excede el umbral permitido."""
|
|
68
85
|
if tasa_peticion > limite:
|
|
69
|
-
# Registrar evento en auditoría
|
|
70
86
|
registrar_evento(
|
|
71
87
|
tipo="DoS",
|
|
72
88
|
descripcion=f"Alta tasa de peticiones desde {ip}: {tasa_peticion} req/min (límite {limite})",
|
|
73
89
|
severidad="ALTA",
|
|
74
90
|
)
|
|
75
|
-
# Mitigación: pasar un usuario genérico
|
|
76
91
|
limitar_peticion(usuario_id="anonimo")
|
|
77
92
|
return True
|
|
93
|
+
elif tasa_peticion > limite * 0.75:
|
|
94
|
+
# Umbral de advertencia
|
|
95
|
+
registrar_evento(
|
|
96
|
+
tipo="DoS",
|
|
97
|
+
descripcion=f"Posible saturación desde {ip}: {tasa_peticion} req/min",
|
|
98
|
+
severidad="MEDIA",
|
|
99
|
+
)
|
|
78
100
|
return False
|
|
79
101
|
|
|
80
102
|
|
|
103
|
+
def analizar_headers(user_agent: str, referer: str) -> List[str]:
|
|
104
|
+
"""Detecta patrones de agentes o cabeceras sospechosas."""
|
|
105
|
+
sospechas = []
|
|
106
|
+
if not user_agent or len(user_agent) < 10:
|
|
107
|
+
sospechas.append("User-Agent vacío o anómalo")
|
|
108
|
+
if "curl" in user_agent.lower() or "python" in user_agent.lower():
|
|
109
|
+
sospechas.append("User-Agent indica script o bot")
|
|
110
|
+
if referer and any(palabra in referer.lower() for palabra in ["attack", "scan", "bot"]):
|
|
111
|
+
sospechas.append("Referer sospechoso")
|
|
112
|
+
return sospechas
|
|
113
|
+
|
|
114
|
+
|
|
81
115
|
# =====================================================
|
|
82
|
-
# ===
|
|
116
|
+
# === MIDDLEWARE PRINCIPAL DE DEFENSA DoS ===
|
|
83
117
|
# =====================================================
|
|
84
118
|
class DOSDefenseMiddleware(MiddlewareMixin):
|
|
85
119
|
"""
|
|
86
|
-
Middleware
|
|
87
|
-
- Captura IP, agente y cabeceras sospechosas.
|
|
88
|
-
- Evalúa la frecuencia de peticiones por IP.
|
|
89
|
-
- Marca request.dos_attack_info con información del intento.
|
|
120
|
+
Middleware de detección y registro de ataques DoS.
|
|
90
121
|
"""
|
|
91
122
|
|
|
92
123
|
def process_request(self, request):
|
|
124
|
+
limpiar_registro_global()
|
|
125
|
+
|
|
93
126
|
client_ip = get_client_ip(request)
|
|
127
|
+
if client_ip in TRUSTED_IPS:
|
|
128
|
+
return None
|
|
129
|
+
|
|
94
130
|
user_agent = request.META.get("HTTP_USER_AGENT", "Desconocido")
|
|
95
131
|
referer = request.META.get("HTTP_REFERER", "")
|
|
96
132
|
path = request.path
|
|
97
133
|
|
|
98
|
-
#
|
|
134
|
+
# Registrar endpoint accedido
|
|
135
|
+
REGISTRO_ENDPOINTS.setdefault(client_ip, set()).add(path)
|
|
136
|
+
|
|
137
|
+
# Mantener ventana deslizante
|
|
99
138
|
limpiar_registro(client_ip)
|
|
139
|
+
REGISTRO_SOLICITUDES[client_ip].append(time.time())
|
|
100
140
|
|
|
101
|
-
# Registrar nueva petición
|
|
102
|
-
REGISTRO_SOLICITUDES.setdefault(client_ip, []).append(time.time())
|
|
103
141
|
tasa = len(REGISTRO_SOLICITUDES[client_ip])
|
|
104
142
|
nivel = calcular_nivel_amenaza_dos(tasa)
|
|
105
143
|
es_dos = detectar_dos(client_ip, tasa)
|
|
106
144
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
145
|
+
descripcion = []
|
|
146
|
+
sospechas_headers = analizar_headers(user_agent, referer)
|
|
147
|
+
if sospechas_headers:
|
|
148
|
+
descripcion.extend(sospechas_headers)
|
|
149
|
+
|
|
150
|
+
# Verificar exceso de endpoints distintos (indicativo de escaneo)
|
|
151
|
+
if len(REGISTRO_ENDPOINTS[client_ip]) > LIMITE_ENDPOINTS_DISTINTOS:
|
|
152
|
+
descripcion.append("Número anormal de endpoints distintos accedidos")
|
|
153
|
+
|
|
154
|
+
if es_dos or descripcion:
|
|
155
|
+
descripcion.insert(0, f"Tasa actual: {tasa} req/min (nivel {nivel:.2f})")
|
|
156
|
+
descripcion.append(f"Ruta: {path}")
|
|
114
157
|
|
|
115
|
-
# Log profesional
|
|
116
158
|
logger.warning(
|
|
117
|
-
"DoS detectado desde IP %s: %s ; nivel: %.2f",
|
|
159
|
+
"DoS detectado o sospechoso desde IP %s: %s ; nivel: %.2f",
|
|
118
160
|
client_ip,
|
|
119
161
|
descripcion,
|
|
120
162
|
nivel,
|
|
121
163
|
)
|
|
122
164
|
|
|
123
|
-
# Enviar a sistema de auditoría
|
|
124
165
|
request.dos_attack_info = {
|
|
125
166
|
"ip": client_ip,
|
|
126
167
|
"tipos": ["DoS"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GuardianUnivalle-Benito-Yucra
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.47
|
|
4
4
|
Summary: Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS, Keylogger) para Django/Flask
|
|
5
5
|
Author-email: Andres Benito Calle Yucra <benitoandrescalle035@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -10,6 +10,7 @@ GuardianUnivalle_Benito_Yucra.egg-info/dependency_links.txt
|
|
|
10
10
|
GuardianUnivalle_Benito_Yucra.egg-info/requires.txt
|
|
11
11
|
GuardianUnivalle_Benito_Yucra.egg-info/top_level.txt
|
|
12
12
|
GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py
|
|
13
|
+
GuardianUnivalle_Benito_Yucra/auditoria/utils_auditoria.py
|
|
13
14
|
GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py
|
|
14
15
|
GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py
|
|
15
16
|
GuardianUnivalle_Benito_Yucra/criptografia/kdf.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GuardianUnivalle-Benito-Yucra
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.47
|
|
4
4
|
Summary: Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS, Keylogger) para Django/Flask
|
|
5
5
|
Author-email: Andres Benito Calle Yucra <benitoandrescalle035@gmail.com>
|
|
6
6
|
License: MIT
|
{guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/pyproject.toml
RENAMED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GuardianUnivalle-Benito-Yucra" # usar mayúsculas consistente
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.47"
|
|
8
8
|
description = "Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS, Keylogger) para Django/Flask"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Andres Benito Calle Yucra", email = "benitoandrescalle035@gmail.com" }
|
guardianunivalle_benito_yucra-0.1.45/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import datetime
|
|
3
|
-
import json
|
|
4
|
-
|
|
5
|
-
LOG_FILE = "auditoria_guardian.log"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def registrar_evento(
|
|
9
|
-
tipo: str,
|
|
10
|
-
descripcion: str = "",
|
|
11
|
-
severidad: str = "MEDIA",
|
|
12
|
-
extra: dict | None = None,
|
|
13
|
-
):
|
|
14
|
-
try:
|
|
15
|
-
evento = {
|
|
16
|
-
"fecha": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
17
|
-
"tipo": tipo,
|
|
18
|
-
"descripcion": descripcion,
|
|
19
|
-
"severidad": severidad,
|
|
20
|
-
"extra": extra or {},
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
# ✅ Crear carpeta solo si hay directorio en la ruta
|
|
24
|
-
log_dir = os.path.dirname(LOG_FILE)
|
|
25
|
-
if log_dir:
|
|
26
|
-
os.makedirs(log_dir, exist_ok=True)
|
|
27
|
-
|
|
28
|
-
with open(LOG_FILE, "a", encoding="utf-8") as f:
|
|
29
|
-
f.write(json.dumps(evento, ensure_ascii=False) + "\n")
|
|
30
|
-
|
|
31
|
-
except Exception as e:
|
|
32
|
-
print(f"[Auditoría] Error al registrar evento: {e}")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def generar_reporte() -> str:
|
|
36
|
-
"""Devuelve todo el contenido del archivo de auditoría."""
|
|
37
|
-
if not os.path.exists(LOG_FILE):
|
|
38
|
-
return "No hay registros aún."
|
|
39
|
-
with open(LOG_FILE, "r", encoding="utf-8") as f:
|
|
40
|
-
return f.read()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|