GuardianUnivalle-Benito-Yucra 0.1.38__py3-none-any.whl → 0.1.39__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_sql.py +62 -125
- {guardianunivalle_benito_yucra-0.1.38.dist-info → guardianunivalle_benito_yucra-0.1.39.dist-info}/METADATA +1 -1
- {guardianunivalle_benito_yucra-0.1.38.dist-info → guardianunivalle_benito_yucra-0.1.39.dist-info}/RECORD +6 -6
- {guardianunivalle_benito_yucra-0.1.38.dist-info → guardianunivalle_benito_yucra-0.1.39.dist-info}/WHEEL +0 -0
- {guardianunivalle_benito_yucra-0.1.38.dist-info → guardianunivalle_benito_yucra-0.1.39.dist-info}/licenses/LICENSE +0 -0
- {guardianunivalle_benito_yucra-0.1.38.dist-info → guardianunivalle_benito_yucra-0.1.39.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# sql_defense.py
|
|
2
|
-
|
|
2
|
+
# sql_defense.py - versión robusta y precisa
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
import re
|
|
6
|
-
from typing import List, Tuple
|
|
7
|
-
from django.conf import settings
|
|
8
6
|
from django.utils.deprecation import MiddlewareMixin
|
|
7
|
+
from django.conf import settings
|
|
9
8
|
|
|
10
|
-
# =====================================================
|
|
11
|
-
# === CONFIGURACIÓN DEL LOGGER ===
|
|
12
|
-
# =====================================================
|
|
13
9
|
logger = logging.getLogger("sqlidefense")
|
|
14
10
|
logger.setLevel(logging.INFO)
|
|
15
11
|
if not logger.handlers:
|
|
@@ -17,154 +13,95 @@ if not logger.handlers:
|
|
|
17
13
|
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
|
18
14
|
logger.addHandler(handler)
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(re.compile(r"
|
|
27
|
-
|
|
28
|
-
(
|
|
29
|
-
re.compile(r"\bselect\b.*\bfrom\b.*\bwhere\b.*\b(or|and)\b.*=", re.I),
|
|
30
|
-
"SELECT con OR/AND",
|
|
31
|
-
),
|
|
32
|
-
# Comparaciones tautológicas (1=1)
|
|
33
|
-
(
|
|
34
|
-
re.compile(r"\b(or|and)\s+\d+\s*=\s*\d+", re.I),
|
|
35
|
-
"Expresión tautológica OR/AND 1=1",
|
|
36
|
-
),
|
|
37
|
-
# Manipulación de tablas
|
|
38
|
-
(
|
|
39
|
-
re.compile(r"\b(drop|truncate|delete|insert|update)\b", re.I),
|
|
40
|
-
"Comando de manipulación de tabla",
|
|
41
|
-
),
|
|
42
|
-
# Comentarios sospechosos o terminadores
|
|
43
|
-
(re.compile(r"(--|#|;)", re.I), "Comentario o terminador sospechoso"),
|
|
44
|
-
# Ejecución directa de procedimientos
|
|
45
|
-
(re.compile(r"exec\s*\(", re.I), "Ejecución de procedimiento almacenado"),
|
|
46
|
-
# Subconsultas y SELECT anidados sospechosos
|
|
47
|
-
(re.compile(r"\(\s*select\b.*\)", re.I), "Subconsulta sospechosa"),
|
|
16
|
+
# Patrones organizados por nivel de severidad
|
|
17
|
+
SQL_PATTERNS = [
|
|
18
|
+
# Ataques fuertes (bloqueo inmediato)
|
|
19
|
+
(re.compile(r"\bunion\b\s+(all\s+)?\bselect\b", re.I), "Uso de UNION SELECT", 0.7),
|
|
20
|
+
(re.compile(r"\bor\b\s+'?\d+'?\s*=\s*'?\d+'?", re.I), "Tautología OR 1=1", 0.6),
|
|
21
|
+
(re.compile(r"\bselect\b.+\bfrom\b", re.I), "Consulta SQL SELECT-FROM", 0.5),
|
|
22
|
+
(re.compile(r"(--|#|/\*|\*/)", re.I), "Comentario SQL sospechoso", 0.4),
|
|
23
|
+
(re.compile(r"\b(drop|truncate|delete|insert|update)\b", re.I), "Manipulación SQL", 0.5),
|
|
24
|
+
(re.compile(r"exec\s*\(", re.I), "Ejecución de procedimiento almacenado", 0.6),
|
|
48
25
|
]
|
|
49
26
|
|
|
27
|
+
# Campos que deben ser analizados con cuidado o ignorados
|
|
28
|
+
IGNORED_FIELDS = ["password", "csrfmiddlewaretoken", "token", "auth"]
|
|
50
29
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"""
|
|
56
|
-
Extrae texto de interés desde el cuerpo, querystring,
|
|
57
|
-
encabezados y referencias para analizar posible SQLi.
|
|
58
|
-
"""
|
|
59
|
-
parts: List[str] = []
|
|
30
|
+
def get_client_ip(request):
|
|
31
|
+
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
32
|
+
if x_forwarded_for:
|
|
33
|
+
return x_forwarded_for.split(",")[0].strip()
|
|
34
|
+
return request.META.get("REMOTE_ADDR", "")
|
|
60
35
|
|
|
36
|
+
def extract_body(request):
|
|
61
37
|
try:
|
|
62
|
-
|
|
63
|
-
if "application/json" in content_type:
|
|
38
|
+
if "application/json" in request.META.get("CONTENT_TYPE", ""):
|
|
64
39
|
data = json.loads(request.body.decode("utf-8") or "{}")
|
|
65
|
-
parts.append(json.dumps(data))
|
|
66
40
|
else:
|
|
67
|
-
|
|
68
|
-
if body:
|
|
69
|
-
parts.append(body)
|
|
41
|
+
data = request.POST.dict() or {}
|
|
70
42
|
except Exception:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
qs = request.META.get("QUERY_STRING", "")
|
|
74
|
-
if qs:
|
|
75
|
-
parts.append(qs)
|
|
76
|
-
|
|
77
|
-
parts.append(request.META.get("HTTP_USER_AGENT", ""))
|
|
78
|
-
parts.append(request.META.get("HTTP_REFERER", ""))
|
|
79
|
-
|
|
80
|
-
return " ".join([p for p in parts if p])
|
|
43
|
+
data = {}
|
|
44
|
+
return data
|
|
81
45
|
|
|
46
|
+
def detect_sql_injection(value: str):
|
|
47
|
+
score_total = 0.0
|
|
48
|
+
descripciones = []
|
|
49
|
+
matches = []
|
|
82
50
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
descripcion: List[str] = []
|
|
51
|
+
for pattern, desc, weight in SQL_PATTERNS:
|
|
52
|
+
if pattern.search(value):
|
|
53
|
+
score_total += weight
|
|
54
|
+
descripciones.append(desc)
|
|
55
|
+
matches.append(pattern.pattern)
|
|
89
56
|
|
|
90
|
-
|
|
91
|
-
if patt.search(text):
|
|
92
|
-
descripcion.append(msg)
|
|
57
|
+
return score_total, descripciones, matches
|
|
93
58
|
|
|
94
|
-
return (len(descripcion) > 0, descripcion)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def get_client_ip(request) -> str:
|
|
98
|
-
"""
|
|
99
|
-
Obtiene la IP real del cliente considerando X-Forwarded-For.
|
|
100
|
-
"""
|
|
101
|
-
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
102
|
-
if x_forwarded_for:
|
|
103
|
-
return x_forwarded_for.split(",")[0].strip()
|
|
104
|
-
return request.META.get("REMOTE_ADDR", "")
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# =====================================================
|
|
108
|
-
# === MIDDLEWARE DE DEFENSA SQLi ===
|
|
109
|
-
# =====================================================
|
|
110
59
|
class SQLIDefenseMiddleware(MiddlewareMixin):
|
|
111
|
-
"""
|
|
112
|
-
Middleware profesional de detección de inyección SQL.
|
|
113
|
-
- Detecta patrones en parámetros, cuerpo y cabeceras.
|
|
114
|
-
- No bloquea directamente; marca el intento para auditoría.
|
|
115
|
-
"""
|
|
116
|
-
|
|
117
60
|
def process_request(self, request):
|
|
118
|
-
# ---------------------------------------------
|
|
119
|
-
# 1. Filtrar IPs confiables
|
|
120
|
-
# ---------------------------------------------
|
|
121
61
|
client_ip = get_client_ip(request)
|
|
122
|
-
trusted_ips
|
|
62
|
+
trusted_ips = getattr(settings, "SQLI_DEFENSE_TRUSTED_IPS", [])
|
|
63
|
+
|
|
123
64
|
if client_ip in trusted_ips:
|
|
124
65
|
return None
|
|
125
66
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if not payload:
|
|
131
|
-
return None
|
|
67
|
+
data = extract_body(request)
|
|
68
|
+
score = 0.0
|
|
69
|
+
all_desc = []
|
|
70
|
+
all_matches = []
|
|
132
71
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
72
|
+
for key, value in data.items():
|
|
73
|
+
if not isinstance(value, str):
|
|
74
|
+
continue
|
|
75
|
+
if key.lower() in IGNORED_FIELDS:
|
|
76
|
+
continue # No analizar contraseñas ni tokens
|
|
77
|
+
|
|
78
|
+
s, desc, matches = detect_sql_injection(value)
|
|
79
|
+
score += s
|
|
80
|
+
all_desc.extend(desc)
|
|
81
|
+
all_matches.extend(matches)
|
|
139
82
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
# ---------------------------------------------
|
|
143
|
-
w_sqli = getattr(settings, "SQLI_DEFENSE_WEIGHT", 0.4)
|
|
144
|
-
detecciones_sqli = len(descripcion)
|
|
145
|
-
s_sqli = w_sqli * detecciones_sqli
|
|
83
|
+
if score == 0:
|
|
84
|
+
return None # nada sospechoso
|
|
146
85
|
|
|
147
|
-
# ---------------------------------------------
|
|
148
|
-
# 5. Registrar e informar el intento
|
|
149
|
-
# ---------------------------------------------
|
|
150
86
|
logger.warning(
|
|
151
|
-
"
|
|
152
|
-
client_ip,
|
|
153
|
-
descripcion,
|
|
154
|
-
payload,
|
|
155
|
-
s_sqli,
|
|
87
|
+
f"[SQLiDetect] IP={client_ip} Score={score:.2f} Desc={all_desc} Campos={list(data.keys())}"
|
|
156
88
|
)
|
|
157
89
|
|
|
158
|
-
#
|
|
90
|
+
# Guardamos info en request
|
|
159
91
|
request.sql_attack_info = {
|
|
160
92
|
"ip": client_ip,
|
|
161
93
|
"tipos": ["SQLi"],
|
|
162
|
-
"descripcion":
|
|
163
|
-
"
|
|
164
|
-
"score": s_sqli,
|
|
94
|
+
"descripcion": all_desc,
|
|
95
|
+
"score": round(score, 2),
|
|
165
96
|
}
|
|
166
97
|
|
|
167
|
-
|
|
98
|
+
# Bloqueamos solo si supera umbral de riesgo
|
|
99
|
+
if score >= 0.7:
|
|
100
|
+
from django.http import JsonResponse
|
|
101
|
+
return JsonResponse(
|
|
102
|
+
{"error": "Posible ataque de inyección SQL detectado. Solicitud bloqueada."},
|
|
103
|
+
status=403
|
|
104
|
+
)
|
|
168
105
|
|
|
169
106
|
|
|
170
107
|
# =====================================================
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GuardianUnivalle-Benito-Yucra
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.39
|
|
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
|
|
@@ -7,14 +7,14 @@ GuardianUnivalle_Benito_Yucra/criptografia/kdf.py,sha256=_sbepEY1qHEKga0ExrX2WRg
|
|
|
7
7
|
GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py,sha256=wt9LRMG9XK4eSSmW91tlGmsJWfyk445b8-n2oxlXlwo,7893
|
|
8
8
|
GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py,sha256=l_JYCmRYpsXt1ZauNPF_wy5uGJhmunRbtJ_WKpC3Otc,6953
|
|
9
9
|
GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py,sha256=L5RQ0Sdgg7hTU1qkZYwt7AcDqtAzT6u-jwBGo7YWfsw,8078
|
|
10
|
-
GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py,sha256=
|
|
10
|
+
GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py,sha256=QiK4GKWK-Kv98ugziymrVqnJOMqcoKcHBByTbAaSr-Y,4244
|
|
11
11
|
GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py,sha256=Ipw1XXBd1-SsOOhhl9joQGduTq0GhSa61TRTyASF3XE,6795
|
|
12
12
|
GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py,sha256=23pLLYqliUoMrIC6ZEwz3hKXeDjWfHSm9vYPWGmDDik,495
|
|
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.39.dist-info/licenses/LICENSE,sha256=5e4IdL542v1E8Ft0A24GZjrxZeTsVK7XrS3mZEUhPtM,37
|
|
17
|
+
guardianunivalle_benito_yucra-0.1.39.dist-info/METADATA,sha256=OPUn0TwR5zL6kpNrPT5Ag_g-BHh9NAClKwt69L8On0g,1893
|
|
18
|
+
guardianunivalle_benito_yucra-0.1.39.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
guardianunivalle_benito_yucra-0.1.39.dist-info/top_level.txt,sha256=HTWfZM64WAV_QYr5cnXnLuabQt92dvlxqlR3pCwpbDQ,30
|
|
20
|
+
guardianunivalle_benito_yucra-0.1.39.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|