GuardianUnivalle-Benito-Yucra 0.1.45__tar.gz → 0.1.46__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.

Files changed (25) hide show
  1. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py +78 -37
  2. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/PKG-INFO +1 -1
  3. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/PKG-INFO +1 -1
  4. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/pyproject.toml +1 -1
  5. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/__init__.py +0 -0
  6. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py +0 -0
  7. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py +0 -0
  8. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py +0 -0
  9. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/criptografia/kdf.py +0 -0
  10. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py +0 -0
  11. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py +0 -0
  12. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py +0 -0
  13. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py +0 -0
  14. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py +0 -0
  15. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/mitigacion/limitador_peticion.py +0 -0
  16. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/mitigacion/lista_bloqueo.py +0 -0
  17. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/puntuacion/puntuacion_amenaza.py +0 -0
  18. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/utilidades.py +0 -0
  19. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/SOURCES.txt +0 -0
  20. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/dependency_links.txt +0 -0
  21. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/requires.txt +0 -0
  22. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/top_level.txt +0 -0
  23. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/LICENSE +0 -0
  24. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/README.md +0 -0
  25. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.46}/setup.cfg +0 -0
@@ -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
- # === CONFIGURACIÓN DEL LOGGER ===
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
- # === PARÁMETROS DE CONFIGURACIÓN BASE ===
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
- # === REGISTRO TEMPORAL DE SOLICITUDES POR IP ===
32
+ # === REGISTRO TEMPORAL EN MEMORIA ===
30
33
  # =====================================================
31
- # En producción se recomienda Redis o Memcached
32
- REGISTRO_SOLICITUDES: Dict[str, List[float]] = {}
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
- # === FUNCIONES AUXILIARES ===
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
- REGISTRO_SOLICITUDES[ip] = [
50
- t for t in REGISTRO_SOLICITUDES.get(ip, []) if ahora - t < VENTANA_SEGUNDOS
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 basada en el peso configurado.
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
- # === MIDDLEWARE DE DETECCIÓN DE DoS ===
116
+ # === MIDDLEWARE PRINCIPAL DE DEFENSA DoS ===
83
117
  # =====================================================
84
118
  class DOSDefenseMiddleware(MiddlewareMixin):
85
119
  """
86
- Middleware para detección y registro de ataques DoS.
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
- # Limpieza de registro anterior
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
- if es_dos:
108
- descripcion = [
109
- f"Tasa de {tasa} req/min excede límite {LIMITE_PETICIONES}",
110
- f"User-Agent: {user_agent}",
111
- f"Referer: {referer or 'N/A'}",
112
- f"Ruta: {path}",
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.45
3
+ Version: 0.1.46
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GuardianUnivalle-Benito-Yucra
3
- Version: 0.1.45
3
+ Version: 0.1.46
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
@@ -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.45"
7
+ version = "0.1.46"
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" }