GuardianUnivalle-Benito-Yucra 0.1.69__py3-none-any.whl → 0.1.71__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 +111 -20
- GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py +86 -39
- GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py +34 -41
- guardianunivalle_benito_yucra-0.1.71.dist-info/METADATA +265 -0
- {guardianunivalle_benito_yucra-0.1.69.dist-info → guardianunivalle_benito_yucra-0.1.71.dist-info}/RECORD +8 -8
- guardianunivalle_benito_yucra-0.1.69.dist-info/METADATA +0 -192
- {guardianunivalle_benito_yucra-0.1.69.dist-info → guardianunivalle_benito_yucra-0.1.71.dist-info}/WHEEL +0 -0
- {guardianunivalle_benito_yucra-0.1.69.dist-info → guardianunivalle_benito_yucra-0.1.71.dist-info}/licenses/LICENSE +0 -0
- {guardianunivalle_benito_yucra-0.1.69.dist-info → guardianunivalle_benito_yucra-0.1.71.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# csrf_defense.py
|
|
2
|
+
# GuardianUnivalle_Benito_Yucra/detectores/csrf_defense.py
|
|
3
|
+
|
|
2
4
|
from __future__ import annotations
|
|
3
5
|
import secrets
|
|
4
6
|
import logging
|
|
5
7
|
import re
|
|
6
8
|
import json
|
|
7
|
-
from typing import List
|
|
9
|
+
from typing import List, Dict, Any
|
|
8
10
|
from urllib.parse import urlparse
|
|
9
11
|
from django.conf import settings
|
|
10
12
|
from django.utils.deprecation import MiddlewareMixin
|
|
@@ -21,21 +23,42 @@ CSRF_HEADER_NAMES = ("HTTP_X_CSRFTOKEN", "HTTP_X_CSRF_TOKEN")
|
|
|
21
23
|
CSRF_COOKIE_NAME = getattr(settings, "CSRF_COOKIE_NAME", "csrftoken")
|
|
22
24
|
POST_FIELD_NAME = "csrfmiddlewaretoken"
|
|
23
25
|
|
|
24
|
-
# Patrón de Content-Type sospechoso
|
|
26
|
+
# Patrón de Content-Type sospechoso - EXPANDIDO
|
|
25
27
|
SUSPICIOUS_CT_PATTERNS = [
|
|
26
28
|
re.compile(r"text/plain", re.I),
|
|
27
29
|
re.compile(r"application/x-www-form-urlencoded", re.I),
|
|
28
30
|
re.compile(r"multipart/form-data", re.I),
|
|
31
|
+
re.compile(r"application/json", re.I),
|
|
32
|
+
re.compile(r"text/html", re.I), # Agregado para HTML CSRF
|
|
29
33
|
]
|
|
30
34
|
|
|
31
|
-
# Parámetros sensibles típicos de CSRF
|
|
35
|
+
# Parámetros sensibles típicos de CSRF - EXPANDIDO
|
|
32
36
|
SENSITIVE_PARAMS = [
|
|
33
|
-
"password", "csrfmiddlewaretoken", "token", "amount", "transfer", "delete", "update"
|
|
37
|
+
"password", "csrfmiddlewaretoken", "token", "amount", "transfer", "delete", "update", "action", "email", "username"
|
|
34
38
|
]
|
|
35
39
|
|
|
40
|
+
# Campos sensibles: ANALIZAMOS COMPLETAMENTE SIN DESCUENTO PARA ROBUSTEZ MÁXIMA
|
|
41
|
+
SENSITIVE_FIELDS = ["password", "csrfmiddlewaretoken", "token", "auth", "email", "username"]
|
|
42
|
+
|
|
36
43
|
CSRF_DEFENSE_MIN_SIGNALS = getattr(settings, "CSRF_DEFENSE_MIN_SIGNALS", 1)
|
|
37
44
|
CSRF_DEFENSE_EXCLUDED_API_PREFIXES = getattr(settings, "CSRF_DEFENSE_EXCLUDED_API_PREFIXES", ["/api/"])
|
|
38
45
|
|
|
46
|
+
# PATRONES EXPANDIDOS PARA ANÁLISIS DE PAYLOAD EN TODOS LOS CAMPOS (SIN DESCUENTO)
|
|
47
|
+
CSRF_PAYLOAD_PATTERNS = [
|
|
48
|
+
(re.compile(r"<script[^>]*>.*?</script>", re.I | re.S), "Script tag en payload", 0.9),
|
|
49
|
+
(re.compile(r"javascript\s*:", re.I), "URI javascript: en payload", 0.8),
|
|
50
|
+
(re.compile(r"http[s]?://[^\s]+", re.I), "URL externa en payload", 0.7),
|
|
51
|
+
(re.compile(r"eval\s*\(", re.I), "eval() en payload", 1.0),
|
|
52
|
+
(re.compile(r"document\.cookie", re.I), "Acceso a cookie en payload", 0.9),
|
|
53
|
+
(re.compile(r"innerHTML\s*=", re.I), "Manipulación DOM innerHTML", 0.8),
|
|
54
|
+
(re.compile(r"XMLHttpRequest", re.I), "XHR en payload", 0.7),
|
|
55
|
+
(re.compile(r"fetch\s*\(", re.I), "fetch() en payload", 0.7),
|
|
56
|
+
(re.compile(r"&#x[0-9a-fA-F]+;", re.I), "Entidades HTML en payload", 0.6),
|
|
57
|
+
(re.compile(r"%3Cscript", re.I), "Script URL-encoded en payload", 0.8),
|
|
58
|
+
(re.compile(r"on\w+\s*=", re.I), "Eventos on* en payload", 0.7),
|
|
59
|
+
(re.compile(r"alert\s*\(", re.I), "alert() en payload (prueba)", 0.5),
|
|
60
|
+
]
|
|
61
|
+
|
|
39
62
|
def get_client_ip(request):
|
|
40
63
|
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
41
64
|
if x_forwarded_for:
|
|
@@ -62,15 +85,15 @@ def origin_matches_host(request) -> bool:
|
|
|
62
85
|
host = host_header.split(":")[0]
|
|
63
86
|
origin = request.META.get("HTTP_ORIGIN", "")
|
|
64
87
|
referer = request.META.get("HTTP_REFERER", "")
|
|
65
|
-
origin_host = host_from_header(origin)
|
|
66
|
-
referer_host = host_from_header(referer)
|
|
67
88
|
# Bloquear obvious javascript: referers
|
|
68
89
|
if any(re.search(r"(javascript:|<script|data:text/html)", h or "", re.I) for h in [origin, referer]):
|
|
69
90
|
return False
|
|
70
|
-
if origin_host
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
91
|
+
if origin_host := host_from_header(origin):
|
|
92
|
+
if origin_host == host:
|
|
93
|
+
return True
|
|
94
|
+
if referer_host := host_from_header(referer):
|
|
95
|
+
if referer_host == host:
|
|
96
|
+
return True
|
|
74
97
|
if not origin and not referer:
|
|
75
98
|
return True
|
|
76
99
|
return False
|
|
@@ -118,6 +141,34 @@ def extract_parameters(request) -> List[str]:
|
|
|
118
141
|
pass
|
|
119
142
|
return params
|
|
120
143
|
|
|
144
|
+
# FUNCIÓN ROBUSTA: Analizar payload en TODOS los campos (incluyendo sensibles sin descuento)
|
|
145
|
+
def analyze_payload(value: str) -> float:
|
|
146
|
+
score = 0.0
|
|
147
|
+
for patt, desc, weight in CSRF_PAYLOAD_PATTERNS:
|
|
148
|
+
if patt.search(value):
|
|
149
|
+
score += weight # Score full, sin descuento
|
|
150
|
+
return round(score, 3)
|
|
151
|
+
|
|
152
|
+
# NUEVA FUNCIÓN: Extraer y analizar query string
|
|
153
|
+
def analyze_query_string(request) -> float:
|
|
154
|
+
qs = request.META.get("QUERY_STRING", "")
|
|
155
|
+
if qs:
|
|
156
|
+
return analyze_payload(qs)
|
|
157
|
+
return 0.0
|
|
158
|
+
|
|
159
|
+
# NUEVA FUNCIÓN: Analizar headers adicionales
|
|
160
|
+
def analyze_headers(request) -> List[str]:
|
|
161
|
+
issues = []
|
|
162
|
+
ua = request.META.get("HTTP_USER_AGENT", "")
|
|
163
|
+
if re.search(r"(script|<|eval|bot|crawler)", ua, re.I):
|
|
164
|
+
issues.append("User-Agent sospechoso (posible automatización/bot)")
|
|
165
|
+
|
|
166
|
+
accept_lang = request.META.get("HTTP_ACCEPT_LANGUAGE", "")
|
|
167
|
+
if not accept_lang or len(accept_lang) < 2:
|
|
168
|
+
issues.append("Accept-Language ausente o muy corto (posible bot)")
|
|
169
|
+
|
|
170
|
+
return issues
|
|
171
|
+
|
|
121
172
|
class CSRFDefenseMiddleware(MiddlewareMixin):
|
|
122
173
|
def process_request(self, request):
|
|
123
174
|
# Excluir APIs JSON si se configuró así
|
|
@@ -136,7 +187,7 @@ class CSRFDefenseMiddleware(MiddlewareMixin):
|
|
|
136
187
|
return None
|
|
137
188
|
|
|
138
189
|
method = (request.method or "").upper()
|
|
139
|
-
if method not in STATE_CHANGING_METHODS:
|
|
190
|
+
if method not in STATE_CHANGING_METHODS: # CORREGIDO: Agregado "in"
|
|
140
191
|
return None
|
|
141
192
|
|
|
142
193
|
descripcion: List[str] = []
|
|
@@ -174,33 +225,73 @@ class CSRFDefenseMiddleware(MiddlewareMixin):
|
|
|
174
225
|
if origin and host_from_header(origin) != (request.META.get("HTTP_HOST") or "").split(":")[0]:
|
|
175
226
|
descripcion.append("JSON POST desde origen externo (posible CSRF)")
|
|
176
227
|
|
|
228
|
+
# 7) Análisis ROBUSTO de payload en TODOS los campos (sin descuento)
|
|
229
|
+
payload_score = 0.0
|
|
230
|
+
payload_summary: List[Dict[str, Any]] = []
|
|
231
|
+
try:
|
|
232
|
+
# Analizar POST
|
|
233
|
+
if hasattr(request, "POST"):
|
|
234
|
+
for key, value in request.POST.items():
|
|
235
|
+
if isinstance(value, str):
|
|
236
|
+
score = analyze_payload(value)
|
|
237
|
+
payload_score += score
|
|
238
|
+
if score > 0:
|
|
239
|
+
payload_summary.append({"field": key, "snippet": value[:300], "score": score})
|
|
240
|
+
# Analizar JSON
|
|
241
|
+
if "application/json" in content_type:
|
|
242
|
+
data = json.loads(request.body.decode("utf-8") or "{}")
|
|
243
|
+
for key, value in data.items():
|
|
244
|
+
if isinstance(value, str):
|
|
245
|
+
score = analyze_payload(value)
|
|
246
|
+
payload_score += score
|
|
247
|
+
if score > 0:
|
|
248
|
+
payload_summary.append({"field": key, "snippet": value[:300], "score": score})
|
|
249
|
+
except Exception as e:
|
|
250
|
+
logger.debug(f"Error analizando payload: {e}")
|
|
251
|
+
|
|
252
|
+
if payload_score > 0:
|
|
253
|
+
descripcion.append(f"Payload sospechoso detectado (score total: {payload_score})")
|
|
254
|
+
|
|
255
|
+
# 8) Análisis de query string
|
|
256
|
+
qs_score = analyze_query_string(request)
|
|
257
|
+
if qs_score > 0:
|
|
258
|
+
descripcion.append(f"Query string sospechosa (score: {qs_score})")
|
|
259
|
+
payload_score += qs_score
|
|
260
|
+
|
|
261
|
+
# 9) Análisis de headers adicionales
|
|
262
|
+
header_issues = analyze_headers(request)
|
|
263
|
+
descripcion.extend(header_issues)
|
|
264
|
+
|
|
177
265
|
# Señales >= umbral => marcar
|
|
178
|
-
|
|
266
|
+
total_signals = len(descripcion)
|
|
267
|
+
if descripcion and total_signals >= CSRF_DEFENSE_MIN_SIGNALS:
|
|
179
268
|
w_csrf = getattr(settings, "CSRF_DEFENSE_WEIGHT", 0.2)
|
|
180
|
-
s_csrf = w_csrf *
|
|
269
|
+
s_csrf = w_csrf * total_signals + payload_score # Score full sin descuento
|
|
181
270
|
request.csrf_attack_info = {
|
|
182
271
|
"ip": client_ip,
|
|
183
272
|
"tipos": ["CSRF"],
|
|
184
273
|
"descripcion": descripcion,
|
|
185
|
-
"payload":
|
|
274
|
+
"payload": json.dumps(payload_summary, ensure_ascii=False)[:1000],
|
|
186
275
|
"score": s_csrf,
|
|
187
276
|
}
|
|
188
277
|
logger.warning(
|
|
189
|
-
"CSRF detectado desde IP %s: %s ; path=%s ; Content-Type=%s ; score=%.2f",
|
|
278
|
+
"CSRF detectado desde IP %s: %s ; path=%s ; Content-Type=%s ; score=%.2f (Ultra-Robust: nada ignorado)",
|
|
190
279
|
client_ip, descripcion, request.path, content_type, s_csrf
|
|
191
280
|
)
|
|
192
281
|
else:
|
|
193
282
|
if descripcion:
|
|
194
|
-
logger.debug(f"[CSRFDefense] low-signals ({
|
|
283
|
+
logger.debug(f"[CSRFDefense] low-signals ({total_signals}) not marking: {descripcion}")
|
|
195
284
|
|
|
196
285
|
return None
|
|
197
286
|
|
|
198
287
|
"""
|
|
199
|
-
CSRF Defense Middleware -
|
|
200
|
-
|
|
288
|
+
CSRF Defense Middleware - Ultra-Robusto (Nada Ignorado)
|
|
289
|
+
=======================================================
|
|
201
290
|
- Detecta múltiples categorías de CSRF: clásico, login, logout, password change, file/action, JSON API.
|
|
202
|
-
- Escanea payloads POST, GET y
|
|
291
|
+
- Escanea payloads POST, GET, JSON, query string y headers, incluyendo TODOS los campos (sensibles y no) con score full (sin descuento).
|
|
203
292
|
- Detecta parámetros sensibles enviados en GET o JSON desde origen externo.
|
|
293
|
+
- Análisis adicional de User-Agent, Accept-Language, y payloads con patrones expandidos.
|
|
204
294
|
- Scoring configurable y logging detallado.
|
|
205
295
|
- Fácil integración con auditoría XSS/SQLi.
|
|
296
|
+
- Máxima robustez: no ignora nada para detección óptima.
|
|
206
297
|
"""
|
|
@@ -8,7 +8,7 @@ from django.utils.deprecation import MiddlewareMixin
|
|
|
8
8
|
from django.conf import settings
|
|
9
9
|
import urllib.parse
|
|
10
10
|
import html
|
|
11
|
-
from typing import List, Tuple, Dict
|
|
11
|
+
from typing import List, Tuple, Dict, Any
|
|
12
12
|
|
|
13
13
|
# ----------------------------
|
|
14
14
|
# Configuración del logger
|
|
@@ -108,9 +108,23 @@ SQL_PATTERNS: List[Tuple[re.Pattern, str, float]] = [
|
|
|
108
108
|
# ------------------ Catch‑all aggressive patterns (usar con cuidado) ------------------
|
|
109
109
|
(re.compile(r"(['\"]).*?;\s*(drop|truncate|delete|update|insert)\b", re.I | re.S), "Cadena con terminador y DDL/DML (potencial ataque)", 0.9),
|
|
110
110
|
(re.compile(r"\b(or)\b\s+1\s*=\s*1\b", re.I), "OR 1=1 tautology", 0.85),
|
|
111
|
+
|
|
112
|
+
# ---------- NUEVOS PATRONES PARA ROBUSTEZ ----------
|
|
113
|
+
(re.compile(r"\b(select\s+.*\s+from\s+.*\s+where\s+.*\s+in\s*\()", re.I | re.S), "Subquery anidada (IN subquery)", 0.75),
|
|
114
|
+
(re.compile(r"\bcase\s+when\s+.*\s+then\s+.*\s+else\b", re.I), "CASE WHEN (blind boolean)", 0.78),
|
|
115
|
+
(re.compile(r"/\*\!.+\*\//", re.I), "Comentarios condicionales MySQL (/*!...*/)", 0.7),
|
|
116
|
+
(re.compile(r"\bif\s*\(\s*.*\s*,\s*.*\s*,\s*.*\s*\)", re.I), "IF() MySQL (conditional)", 0.72),
|
|
117
|
+
(re.compile(r"\bgroup_concat\s*\(", re.I), "GROUP_CONCAT() (exfiltración en error)", 0.8),
|
|
111
118
|
]
|
|
112
119
|
|
|
113
|
-
|
|
120
|
+
# Campos sensibles: ANALIZAMOS COMPLETAMENTE SIN DESCUENTO PARA ROBUSTEZ MÁXIMA
|
|
121
|
+
SENSITIVE_FIELDS = ["password", "csrfmiddlewaretoken", "token", "auth", "email", "username"]
|
|
122
|
+
|
|
123
|
+
DEFAULT_THRESHOLDS = {
|
|
124
|
+
"HIGH": 1.8,
|
|
125
|
+
"MEDIUM": 1.0,
|
|
126
|
+
"LOW": 0.5,
|
|
127
|
+
}
|
|
114
128
|
|
|
115
129
|
# ----------------------------
|
|
116
130
|
# Obtener IP real del cliente
|
|
@@ -124,26 +138,34 @@ def get_client_ip(request):
|
|
|
124
138
|
return request.META.get("REMOTE_ADDR", "")
|
|
125
139
|
|
|
126
140
|
# ----------------------------
|
|
127
|
-
# Extraer payload de la solicitud
|
|
141
|
+
# Extraer payload de la solicitud - MEJORADO PARA ANÁLISIS POR CAMPO
|
|
128
142
|
# ----------------------------
|
|
129
|
-
def
|
|
130
|
-
parts = []
|
|
143
|
+
def extract_payload_as_map(request) -> Dict[str, Any]:
|
|
131
144
|
try:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
ct = request.META.get("CONTENT_TYPE", "")
|
|
146
|
+
if "application/json" in ct:
|
|
147
|
+
raw = request.body.decode("utf-8") or "{}"
|
|
148
|
+
try:
|
|
149
|
+
data = json.loads(raw)
|
|
150
|
+
if isinstance(data, dict):
|
|
151
|
+
return data
|
|
152
|
+
else:
|
|
153
|
+
return {"raw": raw}
|
|
154
|
+
except Exception:
|
|
155
|
+
return {"raw": raw}
|
|
135
156
|
else:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
try:
|
|
158
|
+
post = request.POST.dict()
|
|
159
|
+
if post:
|
|
160
|
+
return post
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|
|
163
|
+
raw = request.body.decode("utf-8", errors="ignore")
|
|
164
|
+
if raw:
|
|
165
|
+
return {"raw": raw}
|
|
139
166
|
except Exception:
|
|
140
167
|
pass
|
|
141
|
-
|
|
142
|
-
qs = request.META.get("QUERY_STRING", "")
|
|
143
|
-
if qs:
|
|
144
|
-
parts.append(qs)
|
|
145
|
-
|
|
146
|
-
return " ".join(parts)
|
|
168
|
+
return {}
|
|
147
169
|
|
|
148
170
|
# ----------------------------
|
|
149
171
|
# Normalización / preprocesamiento
|
|
@@ -159,13 +181,12 @@ def normalize_input(s: str) -> str:
|
|
|
159
181
|
s_dec = html.unescape(s_dec)
|
|
160
182
|
except Exception:
|
|
161
183
|
pass
|
|
162
|
-
# Reemplazo seguro de secuencias \xNN
|
|
163
184
|
s_dec = re.sub(r"\\x([0-9a-fA-F]{2})", r"\\x\g<1>", s_dec)
|
|
164
185
|
s_dec = re.sub(r"\s+", " ", s_dec)
|
|
165
186
|
return s_dec.strip()
|
|
166
187
|
|
|
167
188
|
# ----------------------------
|
|
168
|
-
# Detector SQLi
|
|
189
|
+
# Detector SQLi - ROBUSTO SIN DESCUENTO
|
|
169
190
|
# ----------------------------
|
|
170
191
|
def detect_sql_injection(text: str) -> Dict:
|
|
171
192
|
norm = normalize_input(text or "")
|
|
@@ -174,7 +195,7 @@ def detect_sql_injection(text: str) -> Dict:
|
|
|
174
195
|
descriptions = []
|
|
175
196
|
for pattern, desc, weight in SQL_PATTERNS:
|
|
176
197
|
if pattern.search(norm):
|
|
177
|
-
score += weight
|
|
198
|
+
score += weight # Score full, sin descuento
|
|
178
199
|
matches.append((desc, pattern.pattern, weight))
|
|
179
200
|
descriptions.append(desc)
|
|
180
201
|
|
|
@@ -185,14 +206,8 @@ def detect_sql_injection(text: str) -> Dict:
|
|
|
185
206
|
"sample": norm[:1200],
|
|
186
207
|
}
|
|
187
208
|
|
|
188
|
-
DEFAULT_THRESHOLDS = {
|
|
189
|
-
"HIGH": 1.8,
|
|
190
|
-
"MEDIUM": 1.0,
|
|
191
|
-
"LOW": 0.5,
|
|
192
|
-
}
|
|
193
|
-
|
|
194
209
|
# ----------------------------
|
|
195
|
-
# Middleware SQLi
|
|
210
|
+
# Middleware SQLi - ULTRA-ROBUSTO
|
|
196
211
|
# ----------------------------
|
|
197
212
|
class SQLIDefenseMiddleware(MiddlewareMixin):
|
|
198
213
|
def process_request(self, request):
|
|
@@ -202,33 +217,65 @@ class SQLIDefenseMiddleware(MiddlewareMixin):
|
|
|
202
217
|
|
|
203
218
|
if client_ip in trusted_ips:
|
|
204
219
|
return None
|
|
205
|
-
|
|
206
220
|
referer = request.META.get("HTTP_REFERER", "")
|
|
207
221
|
host = request.get_host()
|
|
208
222
|
if any(url in referer for url in trusted_urls) or any(url in host for url in trusted_urls):
|
|
209
223
|
return None
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
224
|
+
# Extraer datos como mapa para análisis por campo
|
|
225
|
+
data = extract_payload_as_map(request)
|
|
226
|
+
qs = request.META.get("QUERY_STRING", "")
|
|
227
|
+
if qs:
|
|
228
|
+
data["_query_string"] = qs
|
|
229
|
+
if not data:
|
|
230
|
+
return None
|
|
231
|
+
total_score = 0.0
|
|
232
|
+
all_descriptions = []
|
|
233
|
+
all_matches = []
|
|
234
|
+
payload_summary = []
|
|
235
|
+
# Analizar campo por campo - AHORA SIN DESCUENTO PARA ROBUSTEZ
|
|
236
|
+
if isinstance(data, dict):
|
|
237
|
+
for key, value in data.items():
|
|
238
|
+
if isinstance(value, (dict, list)):
|
|
239
|
+
try:
|
|
240
|
+
vtext = json.dumps(value, ensure_ascii=False)
|
|
241
|
+
except Exception:
|
|
242
|
+
vtext = str(value)
|
|
243
|
+
else:
|
|
244
|
+
vtext = str(value or "")
|
|
245
|
+
|
|
246
|
+
result = detect_sql_injection(vtext)
|
|
247
|
+
total_score += result["score"]
|
|
248
|
+
all_descriptions.extend(result["descriptions"])
|
|
249
|
+
all_matches.extend(result["matches"])
|
|
250
|
+
|
|
251
|
+
if result["score"] > 0:
|
|
252
|
+
is_sensitive = isinstance(key, str) and key.lower() in SENSITIVE_FIELDS
|
|
253
|
+
payload_summary.append({"field": key, "snippet": vtext[:300], "sensitive": is_sensitive})
|
|
254
|
+
else:
|
|
255
|
+
raw = str(data)
|
|
256
|
+
result = detect_sql_injection(raw)
|
|
257
|
+
total_score += result["score"]
|
|
258
|
+
all_descriptions.extend(result["descriptions"])
|
|
259
|
+
all_matches.extend(result["matches"])
|
|
260
|
+
if result["score"] > 0:
|
|
261
|
+
payload_summary.append({"field": "raw", "snippet": raw[:500], "sensitive": False})
|
|
262
|
+
|
|
263
|
+
if total_score == 0:
|
|
217
264
|
return None
|
|
218
265
|
|
|
219
266
|
# Registrar ataque
|
|
220
267
|
logger.warning(
|
|
221
268
|
f"[SQLiDetect] IP={client_ip} Host={host} Referer={referer} "
|
|
222
|
-
f"Score={
|
|
269
|
+
f"Score={total_score:.2f} Desc={all_descriptions} Payload={json.dumps(payload_summary, ensure_ascii=False)[:500]}"
|
|
223
270
|
)
|
|
224
271
|
|
|
225
272
|
# Guardar info en request
|
|
226
273
|
request.sql_attack_info = {
|
|
227
274
|
"ip": client_ip,
|
|
228
275
|
"tipos": ["SQLi"],
|
|
229
|
-
"descripcion":
|
|
230
|
-
"payload":
|
|
231
|
-
"score": round(
|
|
276
|
+
"descripcion": all_descriptions,
|
|
277
|
+
"payload": json.dumps(payload_summary, ensure_ascii=False)[:1000],
|
|
278
|
+
"score": round(total_score, 2),
|
|
232
279
|
"url": request.build_absolute_uri(),
|
|
233
280
|
}
|
|
234
281
|
|
|
@@ -29,11 +29,10 @@ except Exception:
|
|
|
29
29
|
_BLEACH_AVAILABLE = False
|
|
30
30
|
|
|
31
31
|
# -------------------------------------------------
|
|
32
|
-
# Patrones XSS con peso (descripcion, peso)
|
|
32
|
+
# Patrones XSS con peso (descripcion, peso) - EXPANDIDOS PARA ROBUSTEZ
|
|
33
33
|
# - pesos mayores = más severo (por ejemplo <script> o javascript:)
|
|
34
|
-
# -
|
|
34
|
+
# - Agregados patrones para DOM-based, polyglots y evasiones avanzadas
|
|
35
35
|
# -------------------------------------------------
|
|
36
|
-
|
|
37
36
|
XSS_PATTERNS: List[Tuple[re.Pattern, str, float]] = [
|
|
38
37
|
# ---------- Máxima severidad / ejecución directa ----------
|
|
39
38
|
(re.compile(r"<\s*script\b", re.I), "Etiqueta <script> (directa)", 0.95),
|
|
@@ -94,20 +93,30 @@ XSS_PATTERNS: List[Tuple[re.Pattern, str, float]] = [
|
|
|
94
93
|
# ---------- Heurísticos de baja severidad (informativo) ----------
|
|
95
94
|
(re.compile(r"<\s*form\b", re.I), "Form (posible vector de ataque relacionado)", 0.25),
|
|
96
95
|
(re.compile(r"(onmouseover|onfocus|onmouseenter|onmouseleave)\b", re.I), "Eventos UI (mouseover/focus)", 0.45),
|
|
96
|
+
|
|
97
|
+
# ---------- NUEVOS PATRONES PARA ROBUSTEZ ----------
|
|
98
|
+
(re.compile(r"\binnerHTML\s*=\s*.*[<>\"']", re.I), "Asignación a innerHTML con tags", 0.85), # DOM-based
|
|
99
|
+
(re.compile(r"\bdocument\.getElementById\s*\(\s*.*\)\.innerHTML", re.I), "Manipulación DOM innerHTML", 0.80),
|
|
100
|
+
(re.compile(r"<script[^>]*src\s*=\s*['\"][^'\"]*['\"][^>]*>", re.I), "Script externo (posible carga remota)", 0.75),
|
|
101
|
+
(re.compile(r"\bXMLHttpRequest\s*\(\s*\)\.open\s*\(\s*['\"](GET|POST)['\"]", re.I), "XHR manipulado (posible exfiltración)", 0.70),
|
|
102
|
+
(re.compile(r"<\s*link\b[^>]*\bhref\s*=\s*['\"][^'\"]*javascript\s*:", re.I), "Link con href javascript:", 0.78),
|
|
103
|
+
(re.compile(r"\bwindow\.open\s*\(\s*['\"]*javascript\s*:", re.I), "window.open con javascript:", 0.82),
|
|
97
104
|
]
|
|
98
105
|
|
|
99
106
|
# -------------------------------------------------
|
|
100
|
-
# Campos
|
|
107
|
+
# Campos sensibles: NO LOS IGNORAMOS COMPLETAMENTE, PERO LES DAMOS DESCUENTO EN SCORE
|
|
108
|
+
# Para robustez, los analizamos pero reducimos el peso para evitar falsos positivos.
|
|
101
109
|
# -------------------------------------------------
|
|
102
|
-
|
|
110
|
+
SENSITIVE_FIELDS = ["password", "csrfmiddlewaretoken", "token", "auth"]
|
|
111
|
+
SENSITIVE_DISCOUNT = 0.5 # Multiplicador para campos sensibles
|
|
103
112
|
|
|
104
113
|
# Umbral por defecto para considerar "alto riesgo" (Auditoria puede bloquear según su lógica)
|
|
105
114
|
XSS_DEFENSE_THRESHOLD = getattr(settings, "XSS_DEFENSE_THRESHOLD", 0.6)
|
|
106
115
|
|
|
107
|
-
|
|
108
116
|
# -------------------------------------------------
|
|
109
117
|
# Util: validación / extracción de IP (robusta)
|
|
110
118
|
# -------------------------------------------------
|
|
119
|
+
|
|
111
120
|
def _is_valid_ip(ip: str) -> bool:
|
|
112
121
|
"""Verifica que la cadena sea una IP válida (v4 o v6)."""
|
|
113
122
|
try:
|
|
@@ -117,7 +126,6 @@ def _is_valid_ip(ip: str) -> bool:
|
|
|
117
126
|
except Exception:
|
|
118
127
|
return False
|
|
119
128
|
|
|
120
|
-
|
|
121
129
|
def get_client_ip(request) -> str:
|
|
122
130
|
"""
|
|
123
131
|
Obtiene la mejor estimación de la IP del cliente:
|
|
@@ -143,7 +151,6 @@ def get_client_ip(request) -> str:
|
|
|
143
151
|
remote = request.META.get("REMOTE_ADDR")
|
|
144
152
|
return remote or ""
|
|
145
153
|
|
|
146
|
-
|
|
147
154
|
# -------------------------------------------------
|
|
148
155
|
# Extraer payload pero evitando cabeceras (para reducir falsos positivos)
|
|
149
156
|
# - Devuelve dict si es JSON, o dict con 'raw' para otros cuerpos
|
|
@@ -185,15 +192,14 @@ def extract_body_as_map(request) -> Dict[str, Any]:
|
|
|
185
192
|
pass
|
|
186
193
|
return {}
|
|
187
194
|
|
|
188
|
-
|
|
189
195
|
# -------------------------------------------------
|
|
190
196
|
# Analizar un solo valor (string) en busca de XSS usando patrones
|
|
191
197
|
# Devuelve (score, descripciones, matches_patterns)
|
|
192
198
|
# -------------------------------------------------
|
|
193
|
-
def detect_xss_in_value(value: str) -> Tuple[float, List[str], List[str]]:
|
|
199
|
+
def detect_xss_in_value(value: str, is_sensitive: bool = False) -> Tuple[float, List[str], List[str]]:
|
|
194
200
|
"""
|
|
195
201
|
Analiza una cadena y devuelve:
|
|
196
|
-
- score acumulado (sum pesos)
|
|
202
|
+
- score acumulado (sum pesos, con descuento si es campo sensible)
|
|
197
203
|
- lista de descripciones activadas
|
|
198
204
|
- lista de patrones (regex.pattern) que matchearon
|
|
199
205
|
"""
|
|
@@ -204,28 +210,26 @@ def detect_xss_in_value(value: str) -> Tuple[float, List[str], List[str]]:
|
|
|
204
210
|
descripcion = []
|
|
205
211
|
matches = []
|
|
206
212
|
|
|
207
|
-
# Si bleach está disponible,
|
|
213
|
+
# Si bleach está disponible, sanitizar y comparar para detección adicional
|
|
214
|
+
if _BLEACH_AVAILABLE:
|
|
215
|
+
cleaned = bleach.clean(value, strip=True)
|
|
216
|
+
if cleaned != value:
|
|
217
|
+
score_total += 0.5 # Penalización por cambios en sanitización
|
|
218
|
+
descripcion.append("Contenido alterado por sanitización (bleach)")
|
|
219
|
+
|
|
208
220
|
for patt, msg, weight in XSS_PATTERNS:
|
|
209
221
|
if patt.search(value):
|
|
210
|
-
|
|
222
|
+
adjusted_weight = weight * SENSITIVE_DISCOUNT if is_sensitive else weight
|
|
223
|
+
score_total += adjusted_weight
|
|
211
224
|
descripcion.append(msg)
|
|
212
225
|
matches.append(patt.pattern)
|
|
213
226
|
|
|
214
227
|
return round(score_total, 3), descripcion, matches
|
|
215
228
|
|
|
216
|
-
|
|
217
229
|
# -------------------------------------------------
|
|
218
|
-
# Middleware principal XSS
|
|
230
|
+
# Middleware principal XSS - MEJORADO PARA ROBUSTEZ
|
|
219
231
|
# -------------------------------------------------
|
|
220
232
|
class XSSDefenseMiddleware(MiddlewareMixin):
|
|
221
|
-
"""
|
|
222
|
-
Middleware para detección XSS.
|
|
223
|
-
- Analiza el body (campo por campo) y querystring si aplica.
|
|
224
|
-
- Ignora campos sensibles (password, token).
|
|
225
|
-
- No incluye User-Agent/Referer en el texto analizado (evita falsos positivos).
|
|
226
|
-
- Añade request.xss_attack_info con: ip, tipos, descripcion, payload, score, url.
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
233
|
def process_request(self, request):
|
|
230
234
|
# 1) IP y exclusiones
|
|
231
235
|
client_ip = get_client_ip(request)
|
|
@@ -251,15 +255,12 @@ class XSSDefenseMiddleware(MiddlewareMixin):
|
|
|
251
255
|
total_score = 0.0
|
|
252
256
|
all_descriptions: List[str] = []
|
|
253
257
|
all_matches: List[str] = []
|
|
254
|
-
# payload_for_storage: guardamos un resumen/truncado para auditoría
|
|
255
258
|
payload_summary = []
|
|
256
259
|
|
|
257
|
-
# 3) Analizar campo por campo (si es dict) o el raw
|
|
260
|
+
# 3) Analizar campo por campo (si es dict) o el raw - AHORA ANALIZA TODO, CON DESCUENTO PARA SENSIBLES
|
|
258
261
|
if isinstance(data, dict):
|
|
259
262
|
for key, value in data.items():
|
|
260
|
-
|
|
261
|
-
if isinstance(key, str) and key.lower() in [f.lower() for f in IGNORED_FIELDS]:
|
|
262
|
-
continue
|
|
263
|
+
is_sensitive = isinstance(key, str) and key.lower() in SENSITIVE_FIELDS
|
|
263
264
|
|
|
264
265
|
# convertir a string si es otro tipo (list, int...)
|
|
265
266
|
if isinstance(value, (dict, list)):
|
|
@@ -270,20 +271,13 @@ class XSSDefenseMiddleware(MiddlewareMixin):
|
|
|
270
271
|
else:
|
|
271
272
|
vtext = str(value or "")
|
|
272
273
|
|
|
273
|
-
|
|
274
|
-
# las probabilidades de XSS son muy bajas; continúa (reduce falsos positivos).
|
|
275
|
-
if key.lower() in ("email", "username") and len(vtext) < 256:
|
|
276
|
-
# aún así pasar por patrones (no lo ignoramos completamente), pero podemos bajar sensibilidad
|
|
277
|
-
pass
|
|
278
|
-
|
|
279
|
-
s, descs, matches = detect_xss_in_value(vtext)
|
|
274
|
+
s, descs, matches = detect_xss_in_value(vtext, is_sensitive)
|
|
280
275
|
total_score += s
|
|
281
276
|
all_descriptions.extend(descs)
|
|
282
277
|
all_matches.extend(matches)
|
|
283
278
|
|
|
284
279
|
if s > 0:
|
|
285
|
-
|
|
286
|
-
payload_summary.append({ "field": key, "snippet": vtext[:300] })
|
|
280
|
+
payload_summary.append({"field": key, "snippet": vtext[:300], "sensitive": is_sensitive})
|
|
287
281
|
|
|
288
282
|
else:
|
|
289
283
|
# si no es dict, analizar el raw como texto
|
|
@@ -293,7 +287,7 @@ class XSSDefenseMiddleware(MiddlewareMixin):
|
|
|
293
287
|
all_descriptions.extend(descs)
|
|
294
288
|
all_matches.extend(matches)
|
|
295
289
|
if s > 0:
|
|
296
|
-
payload_summary.append({"field":"raw","snippet": raw[:500]})
|
|
290
|
+
payload_summary.append({"field": "raw", "snippet": raw[:500], "sensitive": False})
|
|
297
291
|
|
|
298
292
|
# 4) si no detectó nada, continuar
|
|
299
293
|
if total_score == 0:
|
|
@@ -305,7 +299,7 @@ class XSSDefenseMiddleware(MiddlewareMixin):
|
|
|
305
299
|
payload_for_request = json.dumps(payload_summary, ensure_ascii=False)[:2000]
|
|
306
300
|
|
|
307
301
|
logger.warning(
|
|
308
|
-
"XSS detectado desde IP %s URL=%s Score=%.3f Desc=%s",
|
|
302
|
+
"XSS detectado desde IP %s URL=%s Score=%.3f Desc=%s (Robust: incluye sensibles con descuento)",
|
|
309
303
|
client_ip,
|
|
310
304
|
url,
|
|
311
305
|
score_rounded,
|
|
@@ -320,8 +314,7 @@ class XSSDefenseMiddleware(MiddlewareMixin):
|
|
|
320
314
|
"payload": payload_for_request,
|
|
321
315
|
"score": score_rounded,
|
|
322
316
|
"url": url,
|
|
323
|
-
}
|
|
324
|
-
|
|
317
|
+
}
|
|
325
318
|
# 7) NO bloquear aquí — lo hace AuditoriaMiddleware según su política
|
|
326
319
|
return None
|
|
327
320
|
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: GuardianUnivalle-Benito-Yucra
|
|
3
|
+
Version: 0.1.71
|
|
4
|
+
Summary: Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS) para Django/Flask
|
|
5
|
+
Author-email: Andres Benito Calle Yucra <benitoandrescalle035@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/guardianunivalle-benito-yucra/
|
|
8
|
+
Project-URL: Bug_Tracker, https://github.com/Andyyupy/guardianunivalle-benito-yucra/issues
|
|
9
|
+
Keywords: security,django,flask,sqli,xss,csrf,middleware
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Security
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Framework :: Django
|
|
16
|
+
Classifier: Framework :: Flask
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: redis>=4.0
|
|
21
|
+
Requires-Dist: django>=3.2
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
<!-- Información de la librería -->
|
|
25
|
+
<table align="center" style="width: 100%; text-align: center; border-collapse: collapse; background-color: #f4f4f9; border-radius: 15px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); padding: 20px;">
|
|
26
|
+
<tr>
|
|
27
|
+
<td style="border: none; padding: 10px 20px;">
|
|
28
|
+
<img src="https://res.cloudinary.com/dsbgmboh1/image/upload/v1761866594/Andres_Benito_Calle_Yucra_nxyqee.png"
|
|
29
|
+
alt="Univalle Logo" width="300"
|
|
30
|
+
style="transition: transform 0.3s ease-in-out;"
|
|
31
|
+
onmouseover="this.style.transform='scale(1.1)'"
|
|
32
|
+
onmouseout="this.style.transform='scale(1)'">
|
|
33
|
+
</td>
|
|
34
|
+
<td style="border: none; padding: 10px 20px; text-align: center;">
|
|
35
|
+
<h1 style="font-size: 50px; margin: 0; color: #c62828; font-family: 'Arial', sans-serif; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);">
|
|
36
|
+
Guardian Univalle Benito Yucra
|
|
37
|
+
</h1>
|
|
38
|
+
<p style="margin: 5px 0 0 0; font-size: 18px; color: #444; font-family: 'Segoe UI', sans-serif;">
|
|
39
|
+
Framework de detección y defensa de amenazas web para Django.
|
|
40
|
+
</p>
|
|
41
|
+
</td>
|
|
42
|
+
<td style="border: none; padding: 10px 20px;">
|
|
43
|
+
<img src="https://res.cloudinary.com/dsbgmboh1/image/upload/v1761864884/GuardianUnivalle_imeegq.png"
|
|
44
|
+
alt="Django Logo" width="300"
|
|
45
|
+
style="transition: transform 0.3s ease-in-out;"
|
|
46
|
+
onmouseover="this.style.transform='scale(1.1)'"
|
|
47
|
+
onmouseout="this.style.transform='scale(1)'">
|
|
48
|
+
</td>
|
|
49
|
+
</tr>
|
|
50
|
+
</table>
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
`Guardian Univalle` es un sistema de seguridad modular desarrollado para fortalecer aplicaciones Django frente a ataques web comunes como **XSS**, **CSRF**, **inyección SQL**, **ataques DoS** y **scraping automatizado**.
|
|
54
|
+
Cada módulo opera mediante **middlewares independientes** que analizan el tráfico HTTP en tiempo real, aplican heurísticas inteligentes y registran eventos sospechosos para auditoría y bloqueo adaptativo.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
## Arquitectura general
|
|
58
|
+
|
|
59
|
+
Guardian Univalle está diseñado bajo una **arquitectura modular y extensible**, donde cada tipo de amenaza se gestiona mediante un middleware especializado.
|
|
60
|
+
Cada middleware:
|
|
61
|
+
|
|
62
|
+
- Se ejecuta en la fase inicial del request (`process_request`).
|
|
63
|
+
- Analiza cabeceras, cuerpo y metadatos de la petición.
|
|
64
|
+
- Evalúa indicadores de ataque según patrones heurísticos y reglas configurables.
|
|
65
|
+
- Calcula una puntuación de riesgo (score) para cada evento.
|
|
66
|
+
- Anexa la información al objeto `request` (por ejemplo, `request.xss_attack_info`) para que otros módulos (como el de auditoría) la procesen.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Módulos de defensa incluidos
|
|
71
|
+
|
|
72
|
+
### 1. CSRFDefenseMiddleware
|
|
73
|
+
**Defensa contra Cross-Site Request Forgery (CSRF)**
|
|
74
|
+
|
|
75
|
+
Este módulo detecta intentos de falsificación de peticiones mediante:
|
|
76
|
+
|
|
77
|
+
- Verificación de cabeceras **Origin** y **Referer** contra el host real.
|
|
78
|
+
- Validación de **tokens CSRF** en cookies, cabeceras o formularios.
|
|
79
|
+
- Análisis del **tipo de contenido** (`Content-Type`) y parámetros sensibles.
|
|
80
|
+
- Detección de peticiones JSON o formularios enviados desde dominios externos.
|
|
81
|
+
- Asignación de un **score de riesgo** proporcional al número y severidad de señales encontradas.
|
|
82
|
+
|
|
83
|
+
**Algoritmos utilizados:** heurísticas basadas en cabeceras HTTP, validación semántica de origen y detección de anomalías en métodos `POST`, `PUT`, `DELETE` y `PATCH`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### 2. XSSDefenseMiddleware
|
|
88
|
+
**Defensa contra Cross-Site Scripting (XSS)**
|
|
89
|
+
|
|
90
|
+
Analiza los datos enviados en el cuerpo y querystring, detectando vectores de inyección HTML/JS mediante:
|
|
91
|
+
|
|
92
|
+
- Patrones de alto riesgo (`<script>`, `javascript:`, `onload=`, `eval()`).
|
|
93
|
+
- Ofuscaciones con entidades (`<`, `%3Cscript`).
|
|
94
|
+
- Detección de atributos de eventos (`onmouseover`, `onfocus`, etc.).
|
|
95
|
+
- Análisis de URIs maliciosas (`data:text/html`, `vbscript:`).
|
|
96
|
+
- Scoring ponderado por severidad (de 0.3 a 0.95).
|
|
97
|
+
|
|
98
|
+
**Algoritmos utilizados:** expresiones regulares avanzadas con pesos heurísticos y uso opcional de la librería **Bleach** para sanitización comparativa.
|
|
99
|
+
|
|
100
|
+
**Salida:** agrega `request.xss_attack_info` con los detalles de detección, IP de origen, descripción, payload y score total.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### 3. SQLIDefenseMiddleware
|
|
105
|
+
**Defensa contra Inyección SQL (SQLi)**
|
|
106
|
+
|
|
107
|
+
Identifica intentos de inyección SQL en parámetros enviados a través de:
|
|
108
|
+
|
|
109
|
+
- Palabras clave peligrosas (`UNION`, `SELECT`, `DROP`, `INSERT`, `UPDATE`).
|
|
110
|
+
- Uso de comentarios (`--`, `#`, `/* ... */`).
|
|
111
|
+
- Concatenaciones o subconsultas sospechosas.
|
|
112
|
+
- Comportamientos anómalos en parámetros GET, POST o JSON.
|
|
113
|
+
|
|
114
|
+
**Algoritmos utilizados:** heurísticas sintácticas + patrones combinados con contextos.
|
|
115
|
+
Evalúa combinaciones de operadores y palabras reservadas para minimizar falsos positivos.
|
|
116
|
+
|
|
117
|
+
**Resultado:** registra el intento en `request.sql_injection_info` con score calculado y parámetros comprometidos.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### 4. DOSDefenseMiddleware
|
|
122
|
+
**Detección de ataques de Denegación de Servicio (DoS)**
|
|
123
|
+
|
|
124
|
+
Monitorea la frecuencia de peticiones por IP y calcula una métrica adaptativa:
|
|
125
|
+
|
|
126
|
+
- Detecta exceso de solicitudes en intervalos cortos.
|
|
127
|
+
- Analiza `User-Agent`, patrones repetitivos y tamaño de payloads.
|
|
128
|
+
- Aplica límites configurables (`MAX_REQUESTS_PER_WINDOW`).
|
|
129
|
+
- Marca IPs sospechosas para registro y bloqueo temporal.
|
|
130
|
+
|
|
131
|
+
**Algoritmos utilizados:** Sliding Window con conteo adaptativo, controlado por señales de frecuencia e intensidad.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### 5. ScrapingDefenseMiddleware (opcional)
|
|
136
|
+
**Detección de scraping y bots automatizados**
|
|
137
|
+
|
|
138
|
+
Evalúa características típicas de scraping:
|
|
139
|
+
|
|
140
|
+
- User-Agent anómalo o ausente.
|
|
141
|
+
- Patrón de navegación repetitivo o excesivamente rápido.
|
|
142
|
+
- Ausencia de cabeceras humanas como `Accept-Language` o `Referer`.
|
|
143
|
+
- Combinación con heurísticas de DoS para detectar scrapers agresivos.
|
|
144
|
+
|
|
145
|
+
**Algoritmos utilizados:** análisis estadístico de cabeceras + patrones de comportamiento a corto plazo.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Integración y uso
|
|
150
|
+
|
|
151
|
+
### Instalación
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pip install guardian-univalle
|
|
155
|
+
```
|
|
156
|
+
### Configuración en settings.py
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
MIDDLEWARE = [
|
|
160
|
+
# Middlewares personalizados
|
|
161
|
+
"GuardianUnivalle_Benito_Yucra.detectores.detector_dos.DOSDefenseMiddleware",
|
|
162
|
+
"GuardianUnivalle_Benito_Yucra.detectores.detector_sql.SQLIDefenseMiddleware",
|
|
163
|
+
"GuardianUnivalle_Benito_Yucra.detectores.detector_xss.XSSDefenseMiddleware",
|
|
164
|
+
"GuardianUnivalle_Benito_Yucra.detectores.detector_csrf.CSRFDefenseMiddleware",
|
|
165
|
+
"users.middleware.AuditoriaMiddleware",
|
|
166
|
+
"users.auditoria_servidor.AuditoriaServidorMiddleware",
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
ALLOWED_HOSTS = [
|
|
173
|
+
"192.168.0.3",
|
|
174
|
+
"127.0.0.1",
|
|
175
|
+
"localhost",
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
### Parámetros de defensa avanzada
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# --- DoS Defense ---
|
|
183
|
+
DOS_LIMITE_PETICIONES = 120
|
|
184
|
+
DOS_VENTANA_SEGUNDOS = 60
|
|
185
|
+
DOS_PESO = 0.6
|
|
186
|
+
DOS_LIMITE_ENDPOINTS = 80
|
|
187
|
+
DOS_TIEMPO_BLOQUEO = 300
|
|
188
|
+
DOS_TRUSTED_IPS = ["127.0.0.1", "192.168.0.3"]
|
|
189
|
+
|
|
190
|
+
# Score total de bloqueo
|
|
191
|
+
DOS_PESO_BLACKLIST = 0.3
|
|
192
|
+
DOS_PESO_HEURISTICA = 0.1
|
|
193
|
+
DOS_UMBRAL_BLOQUEO = 0.8
|
|
194
|
+
|
|
195
|
+
# Configuración general
|
|
196
|
+
DOS_DEFENSE_MAX_REQUESTS = 100
|
|
197
|
+
DOS_DEFENSE_BLOCK_TIME = 300
|
|
198
|
+
DOS_DEFENSE_TRUSTED_IPS = ["127.0.0.1", "192.168.0.3"]
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# --- SQL Injection Defense ---
|
|
204
|
+
SQLI_DEFENSE_TRUSTED_IPS = ["127.0.0.1", "192.168.0.3"]
|
|
205
|
+
|
|
206
|
+
# --- XSS Defense ---
|
|
207
|
+
XSS_DEFENSE_TRUSTED_IPS = ["127.0.0.1", "192.168.0.3"]
|
|
208
|
+
XSS_DEFENSE_SANITIZE_INPUT = False
|
|
209
|
+
XSS_DEFENSE_BLOCK = True
|
|
210
|
+
XSS_DEFENSE_EXCLUDED_PATHS = ["/health", "/internal"]
|
|
211
|
+
|
|
212
|
+
# --- CSRF Defense ---
|
|
213
|
+
CSRF_DEFENSE_TRUSTED_IPS = ["127.0.0.1", "192.168.0.3"]
|
|
214
|
+
CSRF_DEFENSE_BLOCK = True
|
|
215
|
+
CSRF_DEFENSE_LOG = True
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
### Auditoría y correlación de eventos
|
|
219
|
+
```bash
|
|
220
|
+
request.xss_attack_info = {
|
|
221
|
+
"ip": "192.168.1.10",
|
|
222
|
+
"tipos": ["XSS"],
|
|
223
|
+
"descripcion": ["Etiqueta <script> detectada"],
|
|
224
|
+
"payload": {"field": "comentario", "snippet": "<script>alert(1)</script>"},
|
|
225
|
+
"score": 0.92,
|
|
226
|
+
"url": "/comentarios/enviar/",
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
### Filosofía del proyecto
|
|
231
|
+
Guardian Univalle – Benito & Junkrat busca proporcionar una capa de defensa proactiva para entornos Django universitarios y empresariales, combinando:
|
|
232
|
+
|
|
233
|
+
#Detección heurística.
|
|
234
|
+
|
|
235
|
+
#Análisis semántico de cabeceras y payloads.
|
|
236
|
+
|
|
237
|
+
#Escalamiento de score basado en señales múltiples.
|
|
238
|
+
|
|
239
|
+
Su diseño es didáctico y extensible, ideal tanto para proyectos reales como para enseñanza de ciberseguridad aplicada.
|
|
240
|
+
---
|
|
241
|
+
### Estructura del paquete
|
|
242
|
+
```bash
|
|
243
|
+
guardian_univalle/
|
|
244
|
+
│
|
|
245
|
+
├── detectores/
|
|
246
|
+
│ ├── csrf_defense.py
|
|
247
|
+
│ ├── xss_defense.py
|
|
248
|
+
│ ├── sql_defense.py
|
|
249
|
+
│ ├── dos_defense.py
|
|
250
|
+
│ ├── scraping_defense.py
|
|
251
|
+
│
|
|
252
|
+
├── auditoria/
|
|
253
|
+
│ └── auditoria_middleware.py
|
|
254
|
+
│
|
|
255
|
+
└── __init__.py
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
### Licencia
|
|
259
|
+
Este proyecto se distribuye bajo la licencia MIT, permitiendo libre uso y modificación con atribución.
|
|
260
|
+
|
|
261
|
+
📘 Universidad Privada del Valle – Sede La Paz
|
|
262
|
+
|
|
263
|
+
👨💻 Desarrollado por: Benito Yucra
|
|
264
|
+
|
|
265
|
+
📅 Año: 2025
|
|
@@ -4,16 +4,16 @@ 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=
|
|
7
|
+
GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py,sha256=cVc5-rIwgOuFkov1tHtUR2PnyFdF3Ns6RvJMq4hkuOU,12486
|
|
8
8
|
GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py,sha256=Jy4fhI-6n9wQR0quzpondcUyCA2447lDq4fmOFeM1jA,14989
|
|
9
|
-
GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py,sha256=
|
|
10
|
-
GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py,sha256=
|
|
9
|
+
GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py,sha256=9UZFr68WqzZ7LxouY-4fPkzNWJGcrrB8HDEKUqwP_KQ,14765
|
|
10
|
+
GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py,sha256=azK7pTnO7fxcClEEDoLVBdvWF-hQAvM5KElg9O6Ohkc,14963
|
|
11
11
|
GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py,sha256=23pLLYqliUoMrIC6ZEwz3hKXeDjWfHSm9vYPWGmDDik,495
|
|
12
12
|
GuardianUnivalle_Benito_Yucra/mitigacion/limitador_peticion.py,sha256=ipMOebYhql-6mSyHs0ddYXOcXq9w8P_IXLlpiIqGncw,246
|
|
13
13
|
GuardianUnivalle_Benito_Yucra/mitigacion/lista_bloqueo.py,sha256=6AYWII4mrmwCLHCvGTyoBxR4Oasr4raSHpFbVjqn7d8,193
|
|
14
14
|
GuardianUnivalle_Benito_Yucra/puntuacion/puntuacion_amenaza.py,sha256=Wx5XfcII4oweLvZsTBEJ7kUc9pMpP5-36RfI5C5KJXo,561
|
|
15
|
-
guardianunivalle_benito_yucra-0.1.
|
|
16
|
-
guardianunivalle_benito_yucra-0.1.
|
|
17
|
-
guardianunivalle_benito_yucra-0.1.
|
|
18
|
-
guardianunivalle_benito_yucra-0.1.
|
|
19
|
-
guardianunivalle_benito_yucra-0.1.
|
|
15
|
+
guardianunivalle_benito_yucra-0.1.71.dist-info/licenses/LICENSE,sha256=5e4IdL542v1E8Ft0A24GZjrxZeTsVK7XrS3mZEUhPtM,37
|
|
16
|
+
guardianunivalle_benito_yucra-0.1.71.dist-info/METADATA,sha256=62MKAtQZZ5gi7qyui4-rDZ5sLXyHaS12MvmLWnWBZlQ,9984
|
|
17
|
+
guardianunivalle_benito_yucra-0.1.71.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
guardianunivalle_benito_yucra-0.1.71.dist-info/top_level.txt,sha256=HTWfZM64WAV_QYr5cnXnLuabQt92dvlxqlR3pCwpbDQ,30
|
|
19
|
+
guardianunivalle_benito_yucra-0.1.71.dist-info/RECORD,,
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: GuardianUnivalle-Benito-Yucra
|
|
3
|
-
Version: 0.1.69
|
|
4
|
-
Summary: Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS) para Django/Flask
|
|
5
|
-
Author-email: Andres Benito Calle Yucra <benitoandrescalle035@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://pypi.org/project/guardianunivalle-benito-yucra/
|
|
8
|
-
Project-URL: Bug_Tracker, https://github.com/Andyyupy/guardianunivalle-benito-yucra/issues
|
|
9
|
-
Keywords: security,django,flask,sqli,xss,csrf,middleware
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Topic :: Security
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Framework :: Django
|
|
16
|
-
Classifier: Framework :: Flask
|
|
17
|
-
Requires-Python: >=3.8
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
License-File: LICENSE
|
|
20
|
-
Requires-Dist: redis>=4.0
|
|
21
|
-
Requires-Dist: django>=3.2
|
|
22
|
-
Dynamic: license-file
|
|
23
|
-
|
|
24
|
-
<!-- Información de la librería -->
|
|
25
|
-
|
|
26
|
-
# 🛡️ Guardian Univalle – Benito & Junkrat
|
|
27
|
-
|
|
28
|
-
Framework de detección y defensa de amenazas web para Django y Flask
|
|
29
|
-
|
|
30
|
-
Guardian Univalle es un sistema de seguridad modular desarrollado para fortalecer aplicaciones Django frente a ataques web comunes como XSS, CSRF, inyección SQL, ataques DoS y scraping automatizado.
|
|
31
|
-
Cada módulo opera mediante middleware independientes que analizan el tráfico HTTP en tiempo real, aplican heurísticas inteligentes y registran eventos sospechosos para auditoría y bloqueo adaptativo.
|
|
32
|
-
|
|
33
|
-
⚙️ Arquitectura general
|
|
34
|
-
|
|
35
|
-
Guardian Univalle está diseñado bajo una arquitectura modular y extensible, donde cada tipo de amenaza se gestiona mediante un middleware especializado.
|
|
36
|
-
Cada middleware:
|
|
37
|
-
|
|
38
|
-
- Se ejecuta en la fase inicial del request (process_request).
|
|
39
|
-
- Analiza cabeceras, cuerpo y metadatos de la petición.
|
|
40
|
-
- Evalúa indicadores de ataque según patrones heurísticos y reglas configurables.
|
|
41
|
-
- Calcula una puntuación de riesgo (score) para cada evento.
|
|
42
|
-
- Anexa la información al objeto request (por ejemplo, request.xss_attack_info) para que otros módulos (como el de auditoría) la procesen.
|
|
43
|
-
|
|
44
|
-
🧩 Módulos de defensa incluidos
|
|
45
|
-
|
|
46
|
-
1. 🔐 CSRFDefenseMiddleware
|
|
47
|
-
|
|
48
|
-
Defensa contra Cross-Site Request Forgery (CSRF).
|
|
49
|
-
|
|
50
|
-
Este módulo detecta intentos de falsificación de peticiones mediante:
|
|
51
|
-
|
|
52
|
-
- Verificación de cabeceras Origin y Referer contra el host real.
|
|
53
|
-
- Validación de tokens CSRF en cookies, cabeceras o formularios.
|
|
54
|
-
- Análisis del tipo de contenido (Content-Type) y parámetros sensibles.
|
|
55
|
-
- Detección de peticiones JSON o formularios enviados desde dominios externos.
|
|
56
|
-
- Asignación de un score de riesgo proporcional al número y severidad de señales encontradas.
|
|
57
|
-
|
|
58
|
-
Algoritmos utilizados:
|
|
59
|
-
Heurísticas basadas en cabeceras HTTP, validación semántica de origen, y detección de anomalías en métodos POST, PUT, DELETE y PATCH.
|
|
60
|
-
|
|
61
|
-
2. 🧬 XSSDefenseMiddleware
|
|
62
|
-
|
|
63
|
-
Defensa contra Cross-Site Scripting (XSS).
|
|
64
|
-
|
|
65
|
-
Analiza en profundidad los datos enviados en el cuerpo y querystring, detectando vectores de inyección HTML/JS mediante:
|
|
66
|
-
|
|
67
|
-
- Patrones de alto riesgo (<script>, javascript:, onload=, eval()).
|
|
68
|
-
- Ofuscaciones con entidades (<, %3Cscript).
|
|
69
|
-
- Detección de atributos de eventos (onmouseover, onfocus, etc.).
|
|
70
|
-
- Análisis de URIs maliciosas (data:text/html, vbscript:).
|
|
71
|
-
- Scoring ponderado por severidad (de 0.3 a 0.95).
|
|
72
|
-
|
|
73
|
-
Algoritmos utilizados:
|
|
74
|
-
Detección basada en expresiones regulares avanzadas con pesos heurísticos y uso opcional de la librería Bleach para sanitización comparativa.
|
|
75
|
-
|
|
76
|
-
Salida:
|
|
77
|
-
Agrega request.xss_attack_info con los detalles de detección, la IP de origen, descripción, payload y score total.
|
|
78
|
-
|
|
79
|
-
3. 💾 SQLIDefenseMiddleware
|
|
80
|
-
|
|
81
|
-
Defensa contra Inyección SQL (SQLi).
|
|
82
|
-
|
|
83
|
-
Identifica intentos de inyección SQL en los parámetros enviados a través de:
|
|
84
|
-
|
|
85
|
-
- Palabras clave peligrosas (UNION, SELECT, DROP, INSERT, UPDATE).
|
|
86
|
-
- Uso de comentarios (--, #, /_ ... _/).
|
|
87
|
-
- Concatenaciones o subconsultas sospechosas.
|
|
88
|
-
- Comportamientos anómalos en parámetros GET, POST o JSON.
|
|
89
|
-
|
|
90
|
-
Algoritmos utilizados:
|
|
91
|
-
Heurísticas sintácticas + patrones combinados con contextos.
|
|
92
|
-
Evalúa combinaciones de operadores y palabras reservadas para minimizar falsos positivos.
|
|
93
|
-
|
|
94
|
-
Resultado:
|
|
95
|
-
Registra el intento en request.sql_injection_info con score calculado y parámetros comprometidos.
|
|
96
|
-
|
|
97
|
-
4. 🌐 DOSDefenseMiddleware
|
|
98
|
-
|
|
99
|
-
Detección de ataques de Denegación de Servicio (DoS).
|
|
100
|
-
|
|
101
|
-
Monitorea la frecuencia de peticiones por IP y calcula una métrica adaptativa de comportamiento:
|
|
102
|
-
|
|
103
|
-
- Detecta exceso de solicitudes en intervalos cortos.
|
|
104
|
-
- Analiza User-Agent, patrones repetitivos y tamaño de payloads.
|
|
105
|
-
- Aplica límites configurables (MAX_REQUESTS_PER_WINDOW).
|
|
106
|
-
- Marca IPs sospechosas para registro y bloqueo temporal.
|
|
107
|
-
|
|
108
|
-
Algoritmos utilizados:
|
|
109
|
-
Sliding Window con conteo adaptativo en memoria, controlado por señales de frecuencia e intensidad.
|
|
110
|
-
|
|
111
|
-
5. 🕷️ ScrapingDefenseMiddleware (opcional)
|
|
112
|
-
|
|
113
|
-
Detección de scraping y bots automatizados.
|
|
114
|
-
|
|
115
|
-
Evalúa características típicas de scraping:
|
|
116
|
-
|
|
117
|
-
- User-Agent anómalo o ausente.
|
|
118
|
-
- Patrón de navegación repetitivo o excesivamente rápido.
|
|
119
|
-
- Ausencia de cabeceras humanas (como Accept-Language o Referer).
|
|
120
|
-
- Combinación con heurísticas de DoS para detectar scrapers agresivos.
|
|
121
|
-
|
|
122
|
-
Algoritmos utilizados:
|
|
123
|
-
Análisis estadístico de cabeceras + patrones de comportamiento a corto plazo.
|
|
124
|
-
|
|
125
|
-
🧠 Integración y uso
|
|
126
|
-
|
|
127
|
-
Instalar la librería:
|
|
128
|
-
|
|
129
|
-
pip install guardian-univalle
|
|
130
|
-
|
|
131
|
-
En tu archivo settings.py de Django, añadir los middlewares:
|
|
132
|
-
|
|
133
|
-
MIDDLEWARE = [
|
|
134
|
-
"guardian_univalle.detectores.csrf_defense.CSRFDefenseMiddleware",
|
|
135
|
-
"guardian_univalle.detectores.xss_defense.XSSDefenseMiddleware",
|
|
136
|
-
"guardian_univalle.detectores.sql_defense.SQLIDefenseMiddleware",
|
|
137
|
-
"guardian_univalle.detectores.dos_defense.DOSDefenseMiddleware",
|
|
138
|
-
"guardian_univalle.detectores.scraping_defense.ScrapingDefenseMiddleware", # opcional
|
|
139
|
-
]
|
|
140
|
-
|
|
141
|
-
(Opcional) Configurar umbrales en settings.py:
|
|
142
|
-
|
|
143
|
-
XSS_DEFENSE_THRESHOLD = 0.6
|
|
144
|
-
CSRF_DEFENSE_MIN_SIGNALS = 1
|
|
145
|
-
DOS_DEFENSE_MAX_REQUESTS = 100
|
|
146
|
-
SQLI_DEFENSE_THRESHOLD = 0.5
|
|
147
|
-
|
|
148
|
-
🧾 Auditoría y correlación de eventos
|
|
149
|
-
|
|
150
|
-
Cada middleware genera un diccionario con detalles de detección:
|
|
151
|
-
|
|
152
|
-
request.xss_attack_info = {
|
|
153
|
-
"ip": "192.168.1.10",
|
|
154
|
-
"tipos": ["XSS"],
|
|
155
|
-
"descripcion": ["Etiqueta <script> detectada"],
|
|
156
|
-
"payload": "{'field': 'comentario', 'snippet': '<script>alert(1)</script>'}",
|
|
157
|
-
"score": 0.92,
|
|
158
|
-
"url": "/comentarios/enviar/",
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
Estos datos pueden ser almacenados por un AuditoriaMiddleware o enviados a un sistema SIEM para correlación y respuesta automatizada.
|
|
162
|
-
|
|
163
|
-
🧩 Filosofía del proyecto
|
|
164
|
-
|
|
165
|
-
Guardian Univalle – Benito & Junkrat busca proporcionar una capa de defensa proactiva para entornos Django universitarios y empresariales, combinando:
|
|
166
|
-
|
|
167
|
-
Detección heurística,
|
|
168
|
-
|
|
169
|
-
Análisis semántico de cabeceras y payloads, y
|
|
170
|
-
|
|
171
|
-
Escalamiento de score basado en señales múltiples.
|
|
172
|
-
|
|
173
|
-
Su diseño es didáctico y extensible, ideal tanto para proyectos reales como para enseñanza de ciberseguridad aplicada.
|
|
174
|
-
|
|
175
|
-
🧱 Estructura del paquete
|
|
176
|
-
guardian_univalle/
|
|
177
|
-
│
|
|
178
|
-
├── detectores/
|
|
179
|
-
│ ├── csrf_defense.py
|
|
180
|
-
│ ├── xss_defense.py
|
|
181
|
-
│ ├── sql_defense.py
|
|
182
|
-
│ ├── dos_defense.py
|
|
183
|
-
│ ├── scraping_defense.py
|
|
184
|
-
│
|
|
185
|
-
├── auditoria/
|
|
186
|
-
│ └── auditoria_middleware.py
|
|
187
|
-
│
|
|
188
|
-
└── **init**.py
|
|
189
|
-
|
|
190
|
-
🧾 Licencia
|
|
191
|
-
|
|
192
|
-
Este proyecto se distribuye bajo la licencia MIT, permitiendo libre uso y modificación con atribución.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|