GuardianUnivalle-Benito-Yucra 0.1.29__tar.gz → 0.1.30__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.30/GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py +239 -0
- guardianunivalle_benito_yucra-0.1.30/GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py +166 -0
- guardianunivalle_benito_yucra-0.1.30/GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py +180 -0
- guardianunivalle_benito_yucra-0.1.30/GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py +187 -0
- guardianunivalle_benito_yucra-0.1.30/GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py +197 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra.egg-info/PKG-INFO +1 -1
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/PKG-INFO +1 -1
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/pyproject.toml +1 -1
- guardianunivalle_benito_yucra-0.1.29/GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py +0 -21
- guardianunivalle_benito_yucra-0.1.29/GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py +0 -17
- guardianunivalle_benito_yucra-0.1.29/GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py +0 -21
- guardianunivalle_benito_yucra-0.1.29/GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py +0 -88
- guardianunivalle_benito_yucra-0.1.29/GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py +0 -139
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/__init__.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/criptografia/kdf.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/mitigacion/limitador_peticion.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/mitigacion/lista_bloqueo.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/puntuacion/puntuacion_amenaza.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra/utilidades.py +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra.egg-info/SOURCES.txt +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra.egg-info/dependency_links.txt +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra.egg-info/requires.txt +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/GuardianUnivalle_Benito_Yucra.egg-info/top_level.txt +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/LICENSE +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/README.md +0 -0
- {guardianunivalle_benito_yucra-0.1.29 → guardianunivalle_benito_yucra-0.1.30}/setup.cfg +0 -0
guardianunivalle_benito_yucra-0.1.30/GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CSRF Defense Middleware
|
|
3
|
+
========================
|
|
4
|
+
Detecta y registra posibles ataques CSRF (Cross-Site Request Forgery).
|
|
5
|
+
|
|
6
|
+
Algoritmos relacionados:
|
|
7
|
+
* Uso de secreto aleatorio criptográfico (generar_token_csrf).
|
|
8
|
+
* Validación simple por comparación (validar_token_csrf).
|
|
9
|
+
* Contribución a fórmula de amenaza S:
|
|
10
|
+
S_csrf = w_csrf * intentos_csrf
|
|
11
|
+
S_csrf = 0.2 * 1
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
import secrets
|
|
16
|
+
import logging
|
|
17
|
+
import re
|
|
18
|
+
import json
|
|
19
|
+
from typing import List
|
|
20
|
+
from urllib.parse import urlparse
|
|
21
|
+
from django.conf import settings
|
|
22
|
+
from django.utils.deprecation import MiddlewareMixin
|
|
23
|
+
|
|
24
|
+
# ======================================================
|
|
25
|
+
# === CONFIGURACIÓN DE LOGGER ===
|
|
26
|
+
# ======================================================
|
|
27
|
+
logger = logging.getLogger("csrfdefense")
|
|
28
|
+
logger.setLevel(logging.INFO)
|
|
29
|
+
if not logger.handlers:
|
|
30
|
+
handler = logging.StreamHandler()
|
|
31
|
+
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
32
|
+
logger.addHandler(handler)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ======================================================
|
|
36
|
+
# === FUNCIONES AUXILIARES DE TOKEN CSRF ===
|
|
37
|
+
# ======================================================
|
|
38
|
+
def registrar_evento(tipo: str, mensaje: str):
|
|
39
|
+
"""Registra eventos importantes en los logs."""
|
|
40
|
+
logger.warning(f"[{tipo}] {mensaje}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def generar_token_csrf() -> str:
|
|
44
|
+
"""Genera un token CSRF seguro."""
|
|
45
|
+
token = secrets.token_hex(32)
|
|
46
|
+
registrar_evento("CSRF", "Token CSRF generado")
|
|
47
|
+
return token
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def validar_token_csrf(token: str, token_sesion: str) -> bool:
|
|
51
|
+
"""Valida que el token recibido coincida con el token en sesión."""
|
|
52
|
+
valido = token == token_sesion
|
|
53
|
+
if not valido:
|
|
54
|
+
registrar_evento("CSRF", "Intento de CSRF detectado (token no coincide)")
|
|
55
|
+
return valido
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ======================================================
|
|
59
|
+
# === CONSTANTES Y CONFIGURACIONES ===
|
|
60
|
+
# ======================================================
|
|
61
|
+
STATE_CHANGING_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
|
|
62
|
+
CSRF_HEADER_NAMES = (
|
|
63
|
+
"HTTP_X_CSRFTOKEN",
|
|
64
|
+
"HTTP_X_CSRF_TOKEN",
|
|
65
|
+
)
|
|
66
|
+
CSRF_COOKIE_NAME = getattr(settings, "CSRF_COOKIE_NAME", "csrftoken")
|
|
67
|
+
POST_FIELD_NAME = "csrfmiddlewaretoken"
|
|
68
|
+
|
|
69
|
+
SUSPICIOUS_CT_PATTERNS = [
|
|
70
|
+
re.compile(r"application/x-www-form-urlencoded", re.I),
|
|
71
|
+
re.compile(r"multipart/form-data", re.I),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ======================================================
|
|
76
|
+
# === FUNCIONES DE APOYO ===
|
|
77
|
+
# ======================================================
|
|
78
|
+
def get_client_ip(request):
|
|
79
|
+
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
80
|
+
if x_forwarded_for:
|
|
81
|
+
return x_forwarded_for.split(",")[0].strip()
|
|
82
|
+
return request.META.get("REMOTE_ADDR", "")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def host_from_header(header_value: str) -> str | None:
|
|
86
|
+
if not header_value:
|
|
87
|
+
return None
|
|
88
|
+
try:
|
|
89
|
+
parsed = urlparse(header_value)
|
|
90
|
+
if parsed.netloc:
|
|
91
|
+
return parsed.netloc.split(":")[0]
|
|
92
|
+
return header_value.split(":")[0]
|
|
93
|
+
except Exception:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def origin_matches_host(request) -> bool:
|
|
98
|
+
"""Verifica si Origin/Referer coinciden con Host."""
|
|
99
|
+
host_header = request.META.get("HTTP_HOST") or request.META.get("SERVER_NAME")
|
|
100
|
+
if not host_header:
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
host = host_header.split(":")[0]
|
|
104
|
+
origin = request.META.get("HTTP_ORIGIN", "")
|
|
105
|
+
referer = request.META.get("HTTP_REFERER", "")
|
|
106
|
+
|
|
107
|
+
origin_host = host_from_header(origin)
|
|
108
|
+
referer_host = host_from_header(referer)
|
|
109
|
+
|
|
110
|
+
if origin_host and origin_host == host:
|
|
111
|
+
return True
|
|
112
|
+
if referer_host and referer_host == host:
|
|
113
|
+
return True
|
|
114
|
+
if not origin and not referer:
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def has_csrf_token(request) -> bool:
|
|
121
|
+
"""Comprueba si hay signos de token CSRF presente."""
|
|
122
|
+
for h in CSRF_HEADER_NAMES:
|
|
123
|
+
if request.META.get(h):
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
cookie_val = request.COOKIES.get(CSRF_COOKIE_NAME)
|
|
127
|
+
if cookie_val:
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
if request.method == "POST" and hasattr(request, "POST"):
|
|
132
|
+
if request.POST.get(POST_FIELD_NAME):
|
|
133
|
+
return True
|
|
134
|
+
except Exception:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def extract_payload_text(request) -> str:
|
|
141
|
+
"""Extrae contenido útil de la solicitud para análisis."""
|
|
142
|
+
parts: List[str] = []
|
|
143
|
+
try:
|
|
144
|
+
body = request.body.decode("utf-8", errors="ignore")
|
|
145
|
+
if body:
|
|
146
|
+
parts.append(body)
|
|
147
|
+
except Exception:
|
|
148
|
+
pass
|
|
149
|
+
qs = request.META.get("QUERY_STRING", "")
|
|
150
|
+
if qs:
|
|
151
|
+
parts.append(qs)
|
|
152
|
+
parts.append(request.META.get("HTTP_USER_AGENT", ""))
|
|
153
|
+
parts.append(request.META.get("HTTP_REFERER", ""))
|
|
154
|
+
return " ".join([p for p in parts if p])
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ======================================================
|
|
158
|
+
# === MIDDLEWARE DE DEFENSA CSRF ===
|
|
159
|
+
# ======================================================
|
|
160
|
+
class CSRFDefenseMiddleware(MiddlewareMixin):
|
|
161
|
+
"""
|
|
162
|
+
Middleware para DETECTAR intentos de CSRF:
|
|
163
|
+
- Marca request.sql_attack_info con 'tipos': ['CSRF'] y 'descripcion' con razones.
|
|
164
|
+
- No bloquea la petición directamente, permite que AuditoriaMiddleware lo maneje.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def process_request(self, request):
|
|
168
|
+
client_ip = get_client_ip(request)
|
|
169
|
+
trusted_ips = getattr(settings, "CSRF_DEFENSE_TRUSTED_IPS", [])
|
|
170
|
+
if client_ip in trusted_ips:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
excluded_paths = getattr(settings, "CSRF_DEFENSE_EXCLUDED_PATHS", [])
|
|
174
|
+
if any(request.path.startswith(p) for p in excluded_paths):
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
method = (request.method or "").upper()
|
|
178
|
+
if method not in STATE_CHANGING_METHODS:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
descripcion: List[str] = []
|
|
182
|
+
payload = extract_payload_text(request)
|
|
183
|
+
|
|
184
|
+
# 1) Falta token CSRF
|
|
185
|
+
if not has_csrf_token(request):
|
|
186
|
+
descripcion.append("Falta token CSRF en cookie/header/form")
|
|
187
|
+
|
|
188
|
+
# 2) Origin/Referer no coinciden
|
|
189
|
+
if not origin_matches_host(request):
|
|
190
|
+
descripcion.append(
|
|
191
|
+
"Origin/Referer no coinciden con Host (posible cross-site)"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# 3) Content-Type sospechoso
|
|
195
|
+
content_type = request.META.get("CONTENT_TYPE", "") or ""
|
|
196
|
+
for patt in SUSPICIOUS_CT_PATTERNS:
|
|
197
|
+
if patt.search(content_type):
|
|
198
|
+
descripcion.append(f"Content-Type sospechoso: {content_type}")
|
|
199
|
+
break
|
|
200
|
+
|
|
201
|
+
# 4) Referer ausente
|
|
202
|
+
referer = request.META.get("HTTP_REFERER", "")
|
|
203
|
+
if not referer and not any(request.META.get(h) for h in CSRF_HEADER_NAMES):
|
|
204
|
+
descripcion.append("Referer ausente y sin X-CSRFToken")
|
|
205
|
+
|
|
206
|
+
# Si hay señales, calculamos puntaje y registramos
|
|
207
|
+
if descripcion:
|
|
208
|
+
w_csrf = getattr(settings, "CSRF_DEFENSE_WEIGHT", 0.2)
|
|
209
|
+
intentos_csrf = len(descripcion)
|
|
210
|
+
s_csrf = w_csrf * intentos_csrf
|
|
211
|
+
|
|
212
|
+
request.sql_attack_info = {
|
|
213
|
+
"ip": client_ip,
|
|
214
|
+
"tipos": ["CSRF"],
|
|
215
|
+
"descripcion": descripcion,
|
|
216
|
+
"payload": payload,
|
|
217
|
+
"score": s_csrf,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
logger.warning(
|
|
221
|
+
"CSRF detectado desde IP %s: %s ; payload: %.200s ; score: %.2f",
|
|
222
|
+
client_ip,
|
|
223
|
+
descripcion,
|
|
224
|
+
payload,
|
|
225
|
+
s_csrf,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
Algoritmos relacionados:
|
|
233
|
+
*Uso de secreto aleatorio criptográfico.
|
|
234
|
+
*Opcionalmente derivación con PBKDF2 / Argon2 para reforzar token.
|
|
235
|
+
Contribución a fórmula de amenaza S:
|
|
236
|
+
S_csrf = w_csrf * intentos_csrf
|
|
237
|
+
S_csrf = 0.2 * 1
|
|
238
|
+
donde w_csrf es peso asignado a CSRF y intentos_csrf es la cantidad de intentos detectados.
|
|
239
|
+
"""
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Detector de ataques de tipo DoS (Denial of Service)
|
|
3
|
+
====================================================
|
|
4
|
+
|
|
5
|
+
Este módulo forma parte del sistema de detección de amenazas.
|
|
6
|
+
Detecta tasas de petición anómalas en base a límites configurables,
|
|
7
|
+
captura datos del atacante (IP, agente, cabeceras)
|
|
8
|
+
y registra los incidentes para su auditoría.
|
|
9
|
+
|
|
10
|
+
Componentes:
|
|
11
|
+
- DOSDefenseMiddleware: Middleware principal de detección.
|
|
12
|
+
- detectar_dos(): Evalúa si la tasa supera el umbral permitido.
|
|
13
|
+
- calcular_nivel_amenaza_dos(): Calcula la severidad proporcional.
|
|
14
|
+
- registrar_evento(): Registra los incidentes en auditoría.
|
|
15
|
+
|
|
16
|
+
Algoritmos relacionados:
|
|
17
|
+
* Rate Limiting basado en ventana deslizante.
|
|
18
|
+
* Cálculo de score: S_dos = w_dos * (tasa_peticion / limite)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
import time
|
|
23
|
+
import logging
|
|
24
|
+
import json
|
|
25
|
+
from typing import Dict, List
|
|
26
|
+
from django.conf import settings
|
|
27
|
+
from django.utils.deprecation import MiddlewareMixin
|
|
28
|
+
from ..mitigacion.limitador_peticion import limitar_peticion
|
|
29
|
+
from ..auditoria.registro_auditoria import registrar_evento
|
|
30
|
+
|
|
31
|
+
# =====================================================
|
|
32
|
+
# === CONFIGURACIÓN DEL LOGGER ===
|
|
33
|
+
# =====================================================
|
|
34
|
+
logger = logging.getLogger("dosdefense")
|
|
35
|
+
logger.setLevel(logging.INFO)
|
|
36
|
+
if not logger.handlers:
|
|
37
|
+
handler = logging.StreamHandler()
|
|
38
|
+
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
39
|
+
logger.addHandler(handler)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# =====================================================
|
|
43
|
+
# === PARÁMETROS DE CONFIGURACIÓN BASE ===
|
|
44
|
+
# =====================================================
|
|
45
|
+
LIMITE_PETICIONES = getattr(settings, "DOS_LIMITE_PETICIONES", 100) # por minuto
|
|
46
|
+
VENTANA_SEGUNDOS = getattr(settings, "DOS_VENTANA_SEGUNDOS", 60)
|
|
47
|
+
PESO_DOS = getattr(settings, "DOS_PESO", 0.6)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# =====================================================
|
|
51
|
+
# === REGISTRO TEMPORAL DE SOLICITUDES POR IP ===
|
|
52
|
+
# =====================================================
|
|
53
|
+
# En producción se recomienda usar Redis o Memcached
|
|
54
|
+
# para este tipo de conteo, aquí usamos una memoria temporal.
|
|
55
|
+
REGISTRO_SOLICITUDES: Dict[str, List[float]] = {}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# =====================================================
|
|
59
|
+
# === FUNCIONES AUXILIARES ===
|
|
60
|
+
# =====================================================
|
|
61
|
+
def get_client_ip(request) -> str:
|
|
62
|
+
"""Obtiene la IP real del cliente (considera proxies)."""
|
|
63
|
+
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
64
|
+
if x_forwarded_for:
|
|
65
|
+
return x_forwarded_for.split(",")[0].strip()
|
|
66
|
+
return request.META.get("REMOTE_ADDR", "")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def limpiar_registro(ip: str):
|
|
70
|
+
"""Limpia peticiones antiguas fuera de la ventana de tiempo."""
|
|
71
|
+
ahora = time.time()
|
|
72
|
+
REGISTRO_SOLICITUDES[ip] = [
|
|
73
|
+
t for t in REGISTRO_SOLICITUDES.get(ip, []) if ahora - t < VENTANA_SEGUNDOS
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def calcular_nivel_amenaza_dos(
|
|
78
|
+
tasa_peticion: int, limite: int = LIMITE_PETICIONES
|
|
79
|
+
) -> float:
|
|
80
|
+
"""
|
|
81
|
+
Calcula la puntuación de amenaza DoS basada en el peso configurado.
|
|
82
|
+
Fórmula: S_dos = w_dos * (tasa_peticion / limite)
|
|
83
|
+
"""
|
|
84
|
+
proporcion = tasa_peticion / limite
|
|
85
|
+
s_dos = PESO_DOS * proporcion
|
|
86
|
+
return round(min(s_dos, 1.0), 3)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def detectar_dos(ip: str, tasa_peticion: int, limite: int = LIMITE_PETICIONES) -> bool:
|
|
90
|
+
"""Evalúa si la tasa de peticiones excede el umbral permitido."""
|
|
91
|
+
if tasa_peticion > limite:
|
|
92
|
+
registrar_evento(
|
|
93
|
+
tipo="DoS",
|
|
94
|
+
descripcion=f"Alta tasa de peticiones desde {ip}: {tasa_peticion} req/min (límite {limite})",
|
|
95
|
+
severidad="ALTA",
|
|
96
|
+
)
|
|
97
|
+
limitar_peticion() # Acción de mitigación
|
|
98
|
+
return True
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# =====================================================
|
|
103
|
+
# === MIDDLEWARE DE DETECCIÓN DE DoS ===
|
|
104
|
+
# =====================================================
|
|
105
|
+
class DOSDefenseMiddleware(MiddlewareMixin):
|
|
106
|
+
"""
|
|
107
|
+
Middleware para detección y registro de ataques DoS.
|
|
108
|
+
- Captura IP, agente y cabeceras sospechosas.
|
|
109
|
+
- Evalúa la frecuencia de peticiones por IP.
|
|
110
|
+
- Marca request.sql_attack_info con información del intento.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def process_request(self, request):
|
|
114
|
+
client_ip = get_client_ip(request)
|
|
115
|
+
user_agent = request.META.get("HTTP_USER_AGENT", "Desconocido")
|
|
116
|
+
referer = request.META.get("HTTP_REFERER", "")
|
|
117
|
+
path = request.path
|
|
118
|
+
|
|
119
|
+
# Limpieza de registro anterior
|
|
120
|
+
limpiar_registro(client_ip)
|
|
121
|
+
|
|
122
|
+
# Registrar nueva petición
|
|
123
|
+
REGISTRO_SOLICITUDES.setdefault(client_ip, []).append(time.time())
|
|
124
|
+
tasa = len(REGISTRO_SOLICITUDES[client_ip])
|
|
125
|
+
nivel = calcular_nivel_amenaza_dos(tasa)
|
|
126
|
+
es_dos = detectar_dos(client_ip, tasa)
|
|
127
|
+
|
|
128
|
+
if es_dos:
|
|
129
|
+
descripcion = [
|
|
130
|
+
f"Tasa de {tasa} req/min excede límite {LIMITE_PETICIONES}",
|
|
131
|
+
f"User-Agent: {user_agent}",
|
|
132
|
+
f"Referer: {referer or 'N/A'}",
|
|
133
|
+
f"Ruta: {path}",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
# Log profesional
|
|
137
|
+
logger.warning(
|
|
138
|
+
"DoS detectado desde IP %s: %s ; nivel: %.2f",
|
|
139
|
+
client_ip,
|
|
140
|
+
descripcion,
|
|
141
|
+
nivel,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Enviar a sistema de auditoría
|
|
145
|
+
request.sql_attack_info = {
|
|
146
|
+
"ip": client_ip,
|
|
147
|
+
"tipos": ["DoS"],
|
|
148
|
+
"descripcion": descripcion,
|
|
149
|
+
"payload": json.dumps(
|
|
150
|
+
{"user_agent": user_agent, "referer": referer, "path": path}
|
|
151
|
+
),
|
|
152
|
+
"score": nivel,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
"""
|
|
159
|
+
Algoritmos relacionados:
|
|
160
|
+
*Rate Limiting, listas de bloqueo.
|
|
161
|
+
*Opcional: cifrado de logs con ChaCha20-Poly1305.
|
|
162
|
+
Contribución a fórmula de amenaza S:
|
|
163
|
+
S_dos = w_dos * (tasa_peticion / limite)
|
|
164
|
+
S_dos = 0.6 * (150 / 100)
|
|
165
|
+
donde w_dos es peso asignado a DoS y tasa_peticion / limite es la proporción de la tasa actual sobre el límite.
|
|
166
|
+
"""
|
guardianunivalle_benito_yucra-0.1.30/GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Detector extendido de Keyloggers
|
|
3
|
+
================================
|
|
4
|
+
|
|
5
|
+
Módulo avanzado de detección de keyloggers y software espía en el sistema.
|
|
6
|
+
Incluye revisión de procesos activos, archivos ejecutables sospechosos y
|
|
7
|
+
aplicaciones instaladas en el sistema operativo Windows.
|
|
8
|
+
|
|
9
|
+
Componentes:
|
|
10
|
+
- Escaneo de procesos activos.
|
|
11
|
+
- Detección de archivos con extensiones críticas (.exe, .dll, .scr, .bat, .cmd, .msi).
|
|
12
|
+
- Revisión de aplicaciones instaladas (si se ejecuta en Windows).
|
|
13
|
+
- Cálculo de nivel de amenaza y registro de auditoría.
|
|
14
|
+
|
|
15
|
+
Algoritmos:
|
|
16
|
+
* Revisión de procesos (psutil)
|
|
17
|
+
* Análisis de archivos con extensiones críticas
|
|
18
|
+
* Detección de software instalado
|
|
19
|
+
* Registro cifrado con AES-256 + SHA-512
|
|
20
|
+
* Fórmula: S_keylogger = w_keylogger * (procesos + archivos + instalaciones)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
import psutil
|
|
25
|
+
import os
|
|
26
|
+
import logging
|
|
27
|
+
import platform
|
|
28
|
+
import subprocess
|
|
29
|
+
from typing import List, Dict
|
|
30
|
+
from django.conf import settings
|
|
31
|
+
from ..auditoria.registro_auditoria import registrar_evento
|
|
32
|
+
|
|
33
|
+
# =====================================================
|
|
34
|
+
# === CONFIGURACIÓN DEL LOGGER ===
|
|
35
|
+
# =====================================================
|
|
36
|
+
logger = logging.getLogger("keyloggerdefense")
|
|
37
|
+
logger.setLevel(logging.INFO)
|
|
38
|
+
if not logger.handlers:
|
|
39
|
+
handler = logging.StreamHandler()
|
|
40
|
+
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
41
|
+
logger.addHandler(handler)
|
|
42
|
+
|
|
43
|
+
# =====================================================
|
|
44
|
+
# === CONFIGURACIÓN DE PARÁMETROS ===
|
|
45
|
+
# =====================================================
|
|
46
|
+
PESO_KEYLOGGER = getattr(settings, "KEYLOGGER_PESO", 0.4)
|
|
47
|
+
EXTENSIONES_SOSPECHOSAS = [".exe", ".dll", ".scr", ".bat", ".cmd", ".msi"]
|
|
48
|
+
CARPETAS_CRITICAS = [
|
|
49
|
+
"C:\\Users\\Public",
|
|
50
|
+
"C:\\Users\\%USERNAME%\\AppData\\Roaming",
|
|
51
|
+
"C:\\Users\\%USERNAME%\\AppData\\Local\\Temp",
|
|
52
|
+
"C:\\ProgramData",
|
|
53
|
+
"C:\\Windows\\Temp",
|
|
54
|
+
]
|
|
55
|
+
PATRONES_NOMBRES = ["keylogger", "spy", "hook", "keyboard", "capture", "stealer"]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# =====================================================
|
|
59
|
+
# === FUNCIONES AUXILIARES ===
|
|
60
|
+
# =====================================================
|
|
61
|
+
def calcular_score_keylogger(total_items: int) -> float:
|
|
62
|
+
"""Calcula el nivel de amenaza normalizado."""
|
|
63
|
+
return round(min(PESO_KEYLOGGER * total_items, 1.0), 3)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def detectar_procesos_sospechosos() -> List[Dict]:
|
|
67
|
+
"""Escanea procesos activos y detecta posibles keyloggers."""
|
|
68
|
+
hallazgos = []
|
|
69
|
+
for proc in psutil.process_iter(["pid", "name", "exe"]):
|
|
70
|
+
try:
|
|
71
|
+
nombre = proc.info.get("name", "").lower()
|
|
72
|
+
if any(pat in nombre for pat in PATRONES_NOMBRES):
|
|
73
|
+
hallazgos.append(proc.info)
|
|
74
|
+
registrar_evento("Keylogger", f"Proceso sospechoso: {proc.info}")
|
|
75
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
76
|
+
continue
|
|
77
|
+
return hallazgos
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def detectar_archivos_sospechosos() -> List[str]:
|
|
81
|
+
"""
|
|
82
|
+
Busca archivos con extensiones peligrosas y nombres relacionados
|
|
83
|
+
a keyloggers en carpetas críticas del sistema.
|
|
84
|
+
"""
|
|
85
|
+
hallazgos = []
|
|
86
|
+
for base in CARPETAS_CRITICAS:
|
|
87
|
+
base = os.path.expandvars(base) # reemplaza %USERNAME%
|
|
88
|
+
if not os.path.exists(base):
|
|
89
|
+
continue
|
|
90
|
+
for root, _, files in os.walk(base):
|
|
91
|
+
for file in files:
|
|
92
|
+
if any(file.lower().endswith(ext) for ext in EXTENSIONES_SOSPECHOSAS):
|
|
93
|
+
if any(pat in file.lower() for pat in PATRONES_NOMBRES):
|
|
94
|
+
ruta = os.path.join(root, file)
|
|
95
|
+
hallazgos.append(ruta)
|
|
96
|
+
registrar_evento("Keylogger", f"Archivo sospechoso: {ruta}")
|
|
97
|
+
return hallazgos
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def detectar_programas_instalados() -> List[str]:
|
|
101
|
+
"""
|
|
102
|
+
Analiza programas instalados en el sistema (solo Windows)
|
|
103
|
+
para encontrar software potencialmente malicioso.
|
|
104
|
+
"""
|
|
105
|
+
hallazgos = []
|
|
106
|
+
if platform.system() != "Windows":
|
|
107
|
+
return hallazgos
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
salida = subprocess.check_output(
|
|
111
|
+
["wmic", "product", "get", "name"], stderr=subprocess.DEVNULL
|
|
112
|
+
).decode("utf-8", errors="ignore")
|
|
113
|
+
|
|
114
|
+
for linea in salida.splitlines():
|
|
115
|
+
nombre = linea.strip().lower()
|
|
116
|
+
if any(pat in nombre for pat in PATRONES_NOMBRES):
|
|
117
|
+
hallazgos.append(nombre)
|
|
118
|
+
registrar_evento("Keylogger", f"Software sospechoso: {nombre}")
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.error("Error al listar programas instalados: %s", e)
|
|
121
|
+
|
|
122
|
+
return hallazgos
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# =====================================================
|
|
126
|
+
# === CLASE PRINCIPAL DE DETECCIÓN ===
|
|
127
|
+
# =====================================================
|
|
128
|
+
class KEYLOGGERDefense:
|
|
129
|
+
"""
|
|
130
|
+
Escanea procesos, archivos y programas para detectar keyloggers
|
|
131
|
+
o software espía potencialmente malicioso.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def ejecutar_escaneo(self):
|
|
135
|
+
procesos = detectar_procesos_sospechosos()
|
|
136
|
+
archivos = detectar_archivos_sospechosos()
|
|
137
|
+
programas = detectar_programas_instalados()
|
|
138
|
+
|
|
139
|
+
total_hallazgos = len(procesos) + len(archivos) + len(programas)
|
|
140
|
+
score = calcular_score_keylogger(total_hallazgos)
|
|
141
|
+
|
|
142
|
+
if total_hallazgos > 0:
|
|
143
|
+
descripcion = [
|
|
144
|
+
f"Procesos sospechosos: {len(procesos)}",
|
|
145
|
+
f"Archivos sospechosos: {len(archivos)}",
|
|
146
|
+
f"Programas sospechosos: {len(programas)}",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
logger.warning("Keylogger detectado: %s ; nivel: %.2f", descripcion, score)
|
|
150
|
+
|
|
151
|
+
evento = {
|
|
152
|
+
"tipo": "Keylogger",
|
|
153
|
+
"descripcion": descripcion,
|
|
154
|
+
"procesos": procesos,
|
|
155
|
+
"archivos": archivos,
|
|
156
|
+
"programas": programas,
|
|
157
|
+
"score": score,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
registrar_evento(
|
|
161
|
+
tipo="Keylogger",
|
|
162
|
+
descripcion=f"Detectados {total_hallazgos} elementos sospechosos.",
|
|
163
|
+
severidad="ALTA" if score >= 0.5 else "MEDIA",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return evento
|
|
167
|
+
else:
|
|
168
|
+
logger.info("Sin procesos, archivos o programas sospechosos detectados.")
|
|
169
|
+
return {"tipo": "Keylogger", "descripcion": "Sin hallazgos", "score": 0.0}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
Algoritmos relacionados:
|
|
174
|
+
*Guardar registros con AES-256 + hash SHA-512 para integridad.
|
|
175
|
+
Contribución a fórmula de amenaza S:
|
|
176
|
+
S_keylogger = w_keylogger * numero_procesos_sospechosos
|
|
177
|
+
S_keylogger = 0.4 * 2
|
|
178
|
+
donde w_keylogger es peso asignado a keyloggers y numero_procesos_sospechosos es la cantidad de procesos detectados.
|
|
179
|
+
|
|
180
|
+
"""
|