GuardianUnivalle-Benito-Yucra 0.1.44__py3-none-any.whl → 0.1.46__py3-none-any.whl
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/detectores/detector_csrf.py +36 -65
- GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py +78 -37
- {guardianunivalle_benito_yucra-0.1.44.dist-info → guardianunivalle_benito_yucra-0.1.46.dist-info}/METADATA +1 -1
- {guardianunivalle_benito_yucra-0.1.44.dist-info → guardianunivalle_benito_yucra-0.1.46.dist-info}/RECORD +7 -7
- {guardianunivalle_benito_yucra-0.1.44.dist-info → guardianunivalle_benito_yucra-0.1.46.dist-info}/WHEEL +0 -0
- {guardianunivalle_benito_yucra-0.1.44.dist-info → guardianunivalle_benito_yucra-0.1.46.dist-info}/licenses/LICENSE +0 -0
- {guardianunivalle_benito_yucra-0.1.44.dist-info → guardianunivalle_benito_yucra-0.1.46.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# CSRF defense (parche recomendado)
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import secrets
|
|
4
4
|
import logging
|
|
@@ -9,9 +9,6 @@ from urllib.parse import urlparse
|
|
|
9
9
|
from django.conf import settings
|
|
10
10
|
from django.utils.deprecation import MiddlewareMixin
|
|
11
11
|
|
|
12
|
-
# ======================================================
|
|
13
|
-
# === CONFIGURACIÓN DE LOGGER ===
|
|
14
|
-
# ======================================================
|
|
15
12
|
logger = logging.getLogger("csrfdefense")
|
|
16
13
|
logger.setLevel(logging.INFO)
|
|
17
14
|
if not logger.handlers:
|
|
@@ -19,52 +16,34 @@ if not logger.handlers:
|
|
|
19
16
|
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
20
17
|
logger.addHandler(handler)
|
|
21
18
|
|
|
22
|
-
# ======================================================
|
|
23
|
-
# === FUNCIONES AUXILIARES DE TOKEN CSRF ===
|
|
24
|
-
# ======================================================
|
|
25
|
-
def registrar_evento(tipo: str, mensaje: str):
|
|
26
|
-
"""Registra eventos importantes en los logs."""
|
|
27
|
-
logger.warning(f"[{tipo}] {mensaje}")
|
|
28
|
-
|
|
29
|
-
def generar_token_csrf() -> str:
|
|
30
|
-
"""Genera un token CSRF seguro."""
|
|
31
|
-
token = secrets.token_hex(32)
|
|
32
|
-
registrar_evento("CSRF", "Token CSRF generado")
|
|
33
|
-
return token
|
|
34
|
-
|
|
35
|
-
def validar_token_csrf(token: str, token_sesion: str) -> bool:
|
|
36
|
-
"""Valida que el token recibido coincida con el token en sesión."""
|
|
37
|
-
valido = token == token_sesion
|
|
38
|
-
if not valido:
|
|
39
|
-
registrar_evento("CSRF", "Intento de CSRF detectado (token no coincide)")
|
|
40
|
-
return valido
|
|
41
|
-
|
|
42
|
-
# ======================================================
|
|
43
|
-
# === CONSTANTES Y CONFIGURACIONES ===
|
|
44
|
-
# ======================================================
|
|
45
19
|
STATE_CHANGING_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
|
|
46
20
|
CSRF_HEADER_NAMES = ("HTTP_X_CSRFTOKEN", "HTTP_X_CSRF_TOKEN")
|
|
47
21
|
CSRF_COOKIE_NAME = getattr(settings, "CSRF_COOKIE_NAME", "csrftoken")
|
|
48
22
|
POST_FIELD_NAME = "csrfmiddlewaretoken"
|
|
49
23
|
|
|
50
|
-
#
|
|
24
|
+
# Nota: NO consideramos 'application/json' sospechoso aquí por defecto,
|
|
25
|
+
# porque muchas APIs legítimas usan JSON.
|
|
51
26
|
SUSPICIOUS_CT_PATTERNS = [
|
|
52
27
|
re.compile(r"text/plain", re.I),
|
|
53
|
-
re.compile(r"application/
|
|
28
|
+
re.compile(r"application/x-www-form-urlencoded", re.I),
|
|
29
|
+
re.compile(r"multipart/form-data", re.I),
|
|
54
30
|
]
|
|
55
31
|
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
#
|
|
32
|
+
# Umbral minimo de "señales" para marcar como ataque (configurable)
|
|
33
|
+
CSRF_DEFENSE_MIN_SIGNALS = getattr(settings, "CSRF_DEFENSE_MIN_SIGNALS", 1)
|
|
34
|
+
# Opción para excluir rutas de API que manejan JSON (cambia según tu proyecto)
|
|
35
|
+
CSRF_DEFENSE_EXCLUDED_API_PREFIXES = getattr(settings, "CSRF_DEFENSE_EXCLUDED_API_PREFIXES", ["/api/"])
|
|
36
|
+
|
|
59
37
|
def get_client_ip(request):
|
|
60
|
-
"""Obtiene la IP real del cliente."""
|
|
61
38
|
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
62
39
|
if x_forwarded_for:
|
|
63
|
-
|
|
40
|
+
# toma la primera IP real
|
|
41
|
+
ips = [ip.strip() for ip in x_forwarded_for.split(",") if ip.strip()]
|
|
42
|
+
if ips:
|
|
43
|
+
return ips[0]
|
|
64
44
|
return request.META.get("REMOTE_ADDR", "")
|
|
65
45
|
|
|
66
46
|
def host_from_header(header_value: str) -> str | None:
|
|
67
|
-
"""Extrae el host limpio desde una cabecera."""
|
|
68
47
|
if not header_value:
|
|
69
48
|
return None
|
|
70
49
|
try:
|
|
@@ -76,53 +55,43 @@ def host_from_header(header_value: str) -> str | None:
|
|
|
76
55
|
return None
|
|
77
56
|
|
|
78
57
|
def origin_matches_host(request) -> bool:
|
|
79
|
-
"""Verifica si Origin/Referer coinciden con Host."""
|
|
80
58
|
host_header = request.META.get("HTTP_HOST") or request.META.get("SERVER_NAME")
|
|
81
59
|
if not host_header:
|
|
82
60
|
return True
|
|
83
|
-
|
|
84
61
|
host = host_header.split(":")[0]
|
|
85
62
|
origin = request.META.get("HTTP_ORIGIN", "")
|
|
86
63
|
referer = request.META.get("HTTP_REFERER", "")
|
|
87
|
-
|
|
88
64
|
origin_host = host_from_header(origin)
|
|
89
65
|
referer_host = host_from_header(referer)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if any(re.search(r"(javascript:|<script|data:text/html)", h or "", re.I)
|
|
93
|
-
for h in [origin, referer]):
|
|
66
|
+
# bloquear obvious javascript: referers
|
|
67
|
+
if any(re.search(r"(javascript:|<script|data:text/html)", h or "", re.I) for h in [origin, referer]):
|
|
94
68
|
return False
|
|
95
|
-
|
|
96
69
|
if origin_host and origin_host == host:
|
|
97
70
|
return True
|
|
98
71
|
if referer_host and referer_host == host:
|
|
99
72
|
return True
|
|
73
|
+
# si no hay origin ni referer, lo consideramos neutral (no marcar)
|
|
100
74
|
if not origin and not referer:
|
|
101
75
|
return True
|
|
102
|
-
|
|
103
76
|
return False
|
|
104
77
|
|
|
105
78
|
def has_csrf_token(request) -> bool:
|
|
106
|
-
|
|
79
|
+
# busca header, cookie o campo form
|
|
107
80
|
for h in CSRF_HEADER_NAMES:
|
|
108
81
|
if request.META.get(h):
|
|
109
82
|
return True
|
|
110
|
-
|
|
111
83
|
cookie_val = request.COOKIES.get(CSRF_COOKIE_NAME)
|
|
112
84
|
if cookie_val:
|
|
113
85
|
return True
|
|
114
|
-
|
|
115
86
|
try:
|
|
116
87
|
if request.method == "POST" and hasattr(request, "POST"):
|
|
117
88
|
if request.POST.get(POST_FIELD_NAME):
|
|
118
89
|
return True
|
|
119
90
|
except Exception:
|
|
120
91
|
pass
|
|
121
|
-
|
|
122
92
|
return False
|
|
123
93
|
|
|
124
94
|
def extract_payload_text(request) -> str:
|
|
125
|
-
"""Extrae el cuerpo útil de la solicitud."""
|
|
126
95
|
parts: List[str] = []
|
|
127
96
|
try:
|
|
128
97
|
body = request.body.decode("utf-8", errors="ignore")
|
|
@@ -137,17 +106,15 @@ def extract_payload_text(request) -> str:
|
|
|
137
106
|
parts.append(request.META.get("HTTP_REFERER", ""))
|
|
138
107
|
return " ".join([p for p in parts if p])
|
|
139
108
|
|
|
140
|
-
# ======================================================
|
|
141
|
-
# === MIDDLEWARE DE DEFENSA CSRF ===
|
|
142
|
-
# ======================================================
|
|
143
109
|
class CSRFDefenseMiddleware(MiddlewareMixin):
|
|
144
|
-
"""
|
|
145
|
-
Middleware para DETECTAR intentos de CSRF:
|
|
146
|
-
- Registra request.csrf_attack_info con 'tipos': ['CSRF'] y 'descripcion' con las razones.
|
|
147
|
-
- No bloquea directamente: deja que AuditoriaMiddleware lo maneje.
|
|
148
|
-
"""
|
|
149
|
-
|
|
150
110
|
def process_request(self, request):
|
|
111
|
+
# 1) Excluir APIs JSON si se configuró así
|
|
112
|
+
for prefix in CSRF_DEFENSE_EXCLUDED_API_PREFIXES:
|
|
113
|
+
if request.path.startswith(prefix):
|
|
114
|
+
# debug log opcional
|
|
115
|
+
logger.debug(f"[CSRFDefense] Skip analysis for API prefix {prefix} path {request.path}")
|
|
116
|
+
return None
|
|
117
|
+
|
|
151
118
|
client_ip = get_client_ip(request)
|
|
152
119
|
trusted_ips = getattr(settings, "CSRF_DEFENSE_TRUSTED_IPS", [])
|
|
153
120
|
if client_ip in trusted_ips:
|
|
@@ -172,20 +139,20 @@ class CSRFDefenseMiddleware(MiddlewareMixin):
|
|
|
172
139
|
if not origin_matches_host(request):
|
|
173
140
|
descripcion.append("Origin/Referer no coinciden con Host (posible cross-site)")
|
|
174
141
|
|
|
175
|
-
# 3) Content-Type sospechoso
|
|
176
|
-
content_type = request.META.get("CONTENT_TYPE"
|
|
142
|
+
# 3) Content-Type sospechoso (solo marcaremos si coincide uno de los patterns)
|
|
143
|
+
content_type = (request.META.get("CONTENT_TYPE") or "")
|
|
177
144
|
for patt in SUSPICIOUS_CT_PATTERNS:
|
|
178
145
|
if patt.search(content_type):
|
|
179
146
|
descripcion.append(f"Content-Type sospechoso: {content_type}")
|
|
180
147
|
break
|
|
181
148
|
|
|
182
|
-
# 4) Referer ausente y sin
|
|
149
|
+
# 4) Referer ausente y sin header CSRF
|
|
183
150
|
referer = request.META.get("HTTP_REFERER", "")
|
|
184
151
|
if not referer and not any(request.META.get(h) for h in CSRF_HEADER_NAMES):
|
|
185
152
|
descripcion.append("Referer ausente y sin X-CSRFToken")
|
|
186
153
|
|
|
187
|
-
#
|
|
188
|
-
if descripcion:
|
|
154
|
+
# Si señales >= umbral entonces marcamos para auditoría
|
|
155
|
+
if descripcion and len(descripcion) >= CSRF_DEFENSE_MIN_SIGNALS:
|
|
189
156
|
w_csrf = getattr(settings, "CSRF_DEFENSE_WEIGHT", 0.2)
|
|
190
157
|
intentos_csrf = len(descripcion)
|
|
191
158
|
s_csrf = w_csrf * intentos_csrf
|
|
@@ -199,9 +166,13 @@ class CSRFDefenseMiddleware(MiddlewareMixin):
|
|
|
199
166
|
}
|
|
200
167
|
|
|
201
168
|
logger.warning(
|
|
202
|
-
"CSRF detectado desde IP %s: %s ;
|
|
203
|
-
client_ip, descripcion,
|
|
169
|
+
"CSRF detectado desde IP %s: %s ; path=%s ; Content-Type=%s ; score=%.2f",
|
|
170
|
+
client_ip, descripcion, request.path, content_type, s_csrf
|
|
204
171
|
)
|
|
172
|
+
else:
|
|
173
|
+
# debug útil: saber por qué NO se marcó
|
|
174
|
+
if descripcion:
|
|
175
|
+
logger.debug(f"[CSRFDefense] low-signals ({len(descripcion)}) not marking: {descripcion}")
|
|
205
176
|
|
|
206
177
|
return None
|
|
207
178
|
|
|
@@ -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.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,8 +4,8 @@ GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py,sha256=NnKBOeRWkXV
|
|
|
4
4
|
GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py,sha256=wfoRpaKvOqPbollNQsDNUNWClYJlXYTKTYvv0qcR6aI,962
|
|
5
5
|
GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py,sha256=9djnlzb022hUhrDbQyWz7lWLbkn_vQZ4K7qar1FXYmo,829
|
|
6
6
|
GuardianUnivalle_Benito_Yucra/criptografia/kdf.py,sha256=_sbepEY1qHEKga0ExrX2WRg1HeCPY5MC5CfXZWYyl-A,709
|
|
7
|
-
GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py,sha256=
|
|
8
|
-
GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py,sha256=
|
|
7
|
+
GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py,sha256=q7-UsVseTtIYZz4bbpx2X0kzpDmu2Cetm7eYPJtsruA,7608
|
|
8
|
+
GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py,sha256=167mD_OvicP5T-ptUoBBQlNqFbmL8N1o_4oSKbmcf2Q,8594
|
|
9
9
|
GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py,sha256=L5RQ0Sdgg7hTU1qkZYwt7AcDqtAzT6u-jwBGo7YWfsw,8078
|
|
10
10
|
GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py,sha256=EEbnn5J7sZxnsA2a0cT1VAB4ZS7BMhQiHSeqrR2SU3A,4820
|
|
11
11
|
GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py,sha256=EDxGDaOosFJCyWTS_HkB300qL30ArxAEi-i0cVrzXyU,11027
|
|
@@ -13,8 +13,8 @@ GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py,sha256=23pLLYqliU
|
|
|
13
13
|
GuardianUnivalle_Benito_Yucra/mitigacion/limitador_peticion.py,sha256=ipMOebYhql-6mSyHs0ddYXOcXq9w8P_IXLlpiIqGncw,246
|
|
14
14
|
GuardianUnivalle_Benito_Yucra/mitigacion/lista_bloqueo.py,sha256=6AYWII4mrmwCLHCvGTyoBxR4Oasr4raSHpFbVjqn7d8,193
|
|
15
15
|
GuardianUnivalle_Benito_Yucra/puntuacion/puntuacion_amenaza.py,sha256=Wx5XfcII4oweLvZsTBEJ7kUc9pMpP5-36RfI5C5KJXo,561
|
|
16
|
-
guardianunivalle_benito_yucra-0.1.
|
|
17
|
-
guardianunivalle_benito_yucra-0.1.
|
|
18
|
-
guardianunivalle_benito_yucra-0.1.
|
|
19
|
-
guardianunivalle_benito_yucra-0.1.
|
|
20
|
-
guardianunivalle_benito_yucra-0.1.
|
|
16
|
+
guardianunivalle_benito_yucra-0.1.46.dist-info/licenses/LICENSE,sha256=5e4IdL542v1E8Ft0A24GZjrxZeTsVK7XrS3mZEUhPtM,37
|
|
17
|
+
guardianunivalle_benito_yucra-0.1.46.dist-info/METADATA,sha256=aIfr2HSuO_Tvo3Tvpt3NvLsUA-WG7bBssHew8xnpaXY,1893
|
|
18
|
+
guardianunivalle_benito_yucra-0.1.46.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
guardianunivalle_benito_yucra-0.1.46.dist-info/top_level.txt,sha256=HTWfZM64WAV_QYr5cnXnLuabQt92dvlxqlR3pCwpbDQ,30
|
|
20
|
+
guardianunivalle_benito_yucra-0.1.46.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|