Guía de Seguridad — CreaRack Pro
Guía de Seguridad — CreaRack Pro
Versión: v1.0.51 Última actualización: 07-04-2026 Audiencia: Desarrolladores, administradores, agentes IA
1. Arquitectura de Seguridad
Internet
│
▼
┌─────────────────────┐
│ Hetzner Firewall │ Solo 22 (SSH restringido), 80, 443, ICMP
└─────────────────────┘
│
▼
┌─────────────────────┐
│ Traefik (Reverse │ HTTPS termination, certificados Let's Encrypt
│ Proxy) │ Headers: HSTS, COOP, X-Frame-Options
└─────────────────────┘
│
▼
┌─────────────────────┐
│ Django (Daphne) │ CSP, Permissions-Policy, Rate Limiting
│ Puerto 8000 │ JWT Auth (Agent), Session Auth (Users)
└─────────────────────┘
│
├── PostgreSQL 18 (solo red Docker interna)
├── Valkey (Redis-compatible, con password)
└── VictoriaMetrics (solo red Docker interna)
2. Capas de Protección
2.1 Red (Hetzner Cloud Firewall)
| Regla | Puerto | Origen | Descripción |
|---|---|---|---|
| SSH | 22 | IP trabajo (fija) + rango casa (/16) | Acceso administrativo restringido |
| HTTP | 80 | 0.0.0.0/0 | Redirect a HTTPS |
| HTTPS | 443 | 0.0.0.0/0 | Tráfico web |
| ICMP | — | 0.0.0.0/0 | Ping para monitoreo |
| Default | * | * | DENY |
Todo lo demás queda bloqueado a nivel de cloud provider — ni siquiera llega al VPS.
Dokploy (puerto 3000): Acceso temporal añadiendo regla por IP en Hetzner Firewall. Túnel SSH no funciona (Dokploy valida origin).
GitHub Webhooks (auto-deploy): Para que Dokploy reciba webhooks de GitHub en el puerto 3000, es necesario permitir las 4 subredes de GitHub en iptables DOCKER-USER. Estas reglas se añaden en /root/restore-firewall.sh:
iptables -I DOCKER-USER -s 192.30.252.0/22 -p tcp --dport 3000 -j ACCEPT
iptables -I DOCKER-USER -s 185.199.108.0/22 -p tcp --dport 3000 -j ACCEPT
iptables -I DOCKER-USER -s 140.82.112.0/20 -p tcp --dport 3000 -j ACCEPT
iptables -I DOCKER-USER -s 143.55.64.0/20 -p tcp --dport 3000 -j ACCEPT
Sin estas reglas, el firewall Hetzner puede bloquear el webhook → Dokploy no recibe el push → auto-deploy no se dispara.
2.2 Transporte (HTTPS + HSTS)
# config/settings/production.py
SECURE_HSTS_SECONDS = 31536000 # 1 año
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_COOKIES — Variable de entorno para staging: El servidor de staging corre en HTTP (sin SSL), por lo que las cookies seguras romperían la sesión. La variable de entorno SECURE_COOKIES controla este comportamiento:
| Entorno | Valor | Resultado |
|---|---|---|
| Producción | SECURE_COOKIES=true (default) | SESSION_COOKIE_SECURE=True, CSRF_COOKIE_SECURE=True |
| Staging | SECURE_COOKIES=false | SESSION_COOKIE_SECURE=False, CSRF_COOKIE_SECURE=False |
Configurado en config/settings/production.py y pasado vía panel Dokploy. Nunca usar SECURE_COOKIES=false en producción.
2.3 HTTP Headers
| Header | Valor | Propósito |
|---|---|---|
Content-Security-Policy | default-src 'none'; script-src 'self' 'nonce-{random}'; ... | Previene XSS, controla recursos |
Permissions-Policy | camera=(), microphone=(), geolocation=(), payment=(), usb=(), ... | Bloquea APIs del browser no usadas |
Cross-Origin-Opener-Policy | same-origin | Aísla window context |
X-Frame-Options | DENY | Previene clickjacking |
X-Content-Type-Options | nosniff | Previene MIME sniffing |
Strict-Transport-Security | max-age=31536000; includeSubDomains; preload | Fuerza HTTPS |
Implementación: core/middleware/csp.py (CSP + Permissions-Policy), Django SecurityMiddleware (el resto).
CSP Architecture:
- Nonce-based
script-src: Per-request random nonce (os.urandom(16).hex()). Nounsafe-inline, nounsafe-eval. default-src 'none': Deny-by-default, every resource type explicitly listed.- Path-based: Public pages (login, etc.) get clean HTTPS-only CSP. Agent pages (
/editor/,/terminal/,/monitoring/,/wireless/,/) includehttp://localhost:5050+ws://localhost:5050for Local Agent connectivity. - CSP Level 3 style directives:
style-src-elem 'self' 'unsafe-inline'for<style>elements (HTMX dynamic) +style-src-attr 'unsafe-inline'forstyle=""attributes (ECharts, Konva). Nostyle-srcdirective — Observatory doesn’t flag granular directives. - Mozilla Observatory: A+ (125/100), 10/10 tests passed.
- Delegated handlers: ~330 inline event handlers converted to
data-actionsystem inbase.js. Seearchive/plans/CSP_MIGRATION_PLAN.md.
2.4 Rate Limiting
# config/settings/base.py
RATE_LIMIT_REQUESTS = 100 # Max requests por ventana
RATE_LIMIT_WINDOW = 60 # Ventana en segundos
RATE_LIMIT_ENDPOINTS = {
'/accounts/login/': {'requests': 5, 'window': 60},
'/api/auth/passkey-options/': {'requests': 10, 'window': 60},
'/api/auth/validate-and-setup-biometric/': {'requests': 5, 'window': 60},
'/admin/login/': {'requests': 5, 'window': 60},
'/api/blueprints/auto-plan': {'requests': 10, 'window': 60},
}
IP Spoofing Protection: X-Forwarded-For solo se acepta desde proxies confiados (Traefik gateway 127.0.0.1, 172.17.0.1). Peticiones directas usan REMOTE_ADDR.
Implementación: core/middleware/rate_limit.py
2.5 Endpoint Protection
| Endpoint | Protección | Archivo |
|---|---|---|
/metrics | Solo IPs internas (127.x, 172.16.x, 10.x, 192.168.x) | core/middleware/metrics_restrict.py |
/api/agent/diag/oids/ | JWT Agent auth obligatoria | terminal/api/sentinel.py |
/health | Sin auth, errores sanitizados (no leaks) | core/views.py |
/ready, /alive | Sin auth, sin CSRF (probes de orchestrador) | core/views.py |
3. Autenticación
3.1 Sesiones (Usuarios Web)
| Setting | Valor | Razón |
|---|---|---|
| Engine | django.contrib.sessions.backends.cache | Valkey-backed, rápido |
| HttpOnly | True | JavaScript no puede leer la cookie |
| SameSite | Strict | Máxima protección CSRF |
| Secure | True (producción) | Solo por HTTPS |
| Edad | 2 semanas | Balance seguridad/comodidad |
3.2 JWT (Agent Local)
| Aspecto | Valor |
|---|---|
| Algoritmo | HS256 (explícito en encode y decode) |
| Access token | 72 horas |
| Refresh token | 180 días |
| Almacenamiento | Windows DPAPI (cifrado en disco) |
| Verificación | Server-side en cada WebSocket connect |
3.3 Passkeys/WebAuthn
- Challenge:
os.urandom(32)(criptográficamente seguro) - Tokens single-use (consumed before processing)
- Origin verification contra RP ID
- Protección contra duplicate registration
3.4 Passwords
4 validadores activos: similitud con username, longitud mínima 8, diccionario de comunes, solo numéricos.
4. Multi-Tenancy (Aislamiento de Datos)
CreaRack Pro usa dos capas de aislamiento que trabajan conjuntamente:
- Capa aplicación (Django ORM) — filtros
organization=orgen todos los queries - Capa base de datos (PostgreSQL RLS) — políticas Row-Level Security como red de seguridad
4.1 Patrón Estándar (Capa ORM)
Todos los queries deben filtrar por organización:
# CORRECTO — filtra por org del usuario
device = get_object_or_404(Device, id=device_id, rack__organization=org)
# INCORRECTO — acceso a cualquier recurso por ID
device = get_object_or_404(Device, id=device_id)
4.2 get_current_org()
# core/utils/organization.py
def get_current_org(request):
if request.user.is_authenticated and hasattr(request.user, 'organization'):
return request.user.organization
return None # Callers DEBEN manejar None
IMPORTANTE: No hay fallback. Si el usuario no tiene organización, retorna None. Todos los endpoints deben verificar:
org = get_current_org(request)
if not org:
return 400, {"error": "No organization context"}
4.3 Stencils (caso especial)
Los stencils de sistema (organization=NULL) son compartidos. Usar:
from django.db.models import Q
stencil = Stencil.objects.get(
Q(organization=org) | Q(organization__isnull=True),
id=stencil_id,
)
4.4 PostgreSQL Row-Level Security (Capa DB)
Implementado en v1.0.50 (03-04-2026). Migration
core/0017_rls_policies.
RLS actúa como red de seguridad: si un bug en el ORM omite el filtro por organización, PostgreSQL bloquea el acceso igualmente.
Mecanismo:
TenantRLSMiddleware(después deModuleGatingMiddleware) ejecutaSET app.current_org_id = '{org_id}'al inicio de cada request- PostgreSQL aplica la política
USING (organization_id = current_setting('app.current_org_id')::int)en cada SELECT/UPDATE/DELETE - Al terminar el request,
RESET app.current_org_idpreviene leakage en conexiones pooled (CONN_MAX_AGE=600)
Cobertura: 35 tablas con políticas RLS:
- 29 tablas con org no-nullable: Solo ven filas de su organización
- 6 tablas con org nullable (Stencil, BoxCategory, User, SystemLog, ScriptTemplate, MonitoringAlert): Ven filas propias + globales (
org IS NULL)
Bypass:
- Superusers:
org_id=0→ ven todas las filas - Requests sin auth:
org_id=0→ bypass (páginas públicas) - Nota: Las tablas con FK heredada (Device→Rack→Org) no necesitan política propia — la integridad referencial protege automáticamente
Verificación:
-- Comprobar que RLS está activo en una tabla
SELECT relname, relrowsecurity, relforcerowsecurity
FROM pg_class WHERE relname = 'racks_rack';
-- Comprobar política
SELECT * FROM pg_policies WHERE tablename = 'racks_rack';
4.5 Ciclo de Vida del Tenant
| Fase | Protección |
|---|---|
| Onboarding | POST /api/signup — transaction atómica, plan starter, módulos auto-sync |
| Operación | ORM filters + RLS policies + Module Gating middleware |
| Soft-delete | deleted_at set → org inaccesible pero datos preservados |
| Purge | Huey task purge_deleted_organizations — hard-delete tras 90 días + backup files |
| Integridad | Huey task check_tenant_integrity — detecta anomalías cross-tenant diariamente |
4.6 Backups Per-Tenant
- Huey task
run_org_backupsgenera backup diario (3:30 AM) por organización activa - Storage:
MEDIA_ROOT/backups/{org_id}/backup_YYYY-MM-DD.zip - Retención configurable:
Organization.backup_retention_days(0=default plan: Starter 7d, Pro 30d) - Descarga manual:
GET /api/backup/latest(admin only) - Formato: ZIP con
backup_data.json(14 entity types) +media/uploads/
5. SSRF Protection (Webhooks)
Los Notification Channels de ITSM permiten al usuario configurar URLs de webhook. El servidor hace HTTP POST a esas URLs, creando un vector SSRF.
5.1 Validación de URLs
# monitoring/services/notification_service.py
def validate_webhook_url(url):
# 1. Solo esquemas http/https
# 2. Resolver DNS del hostname
# 3. Verificar que la IP resuelta NO esté en redes bloqueadas:
# - 127.0.0.0/8 (loopback)
# - 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (privadas)
# - 169.254.0.0/16 (link-local + cloud metadata)
# - ::1, fc00::/7, fe80::/10 (IPv6 privadas)
# 4. Bloquear hostnames conocidos: localhost, metadata.google.internal
5.2 Protecciones Adicionales
| Medida | Valor |
|---|---|
| Timeout | 3 segundos |
| Redirects | Deshabilitados (allow_redirects=False) |
| Máx response | 1 MB (configurado, no implementado en stream) |
6. Mass Assignment Protection
6.1 Patrón Seguro (Whitelist)
# CORRECTO — whitelist explícito
UPDATABLE_FIELDS = {'display_name', 'aliases', 'priority', ...}
for key, value in data.items():
if key in UPDATABLE_FIELDS:
setattr(vendor, key, value)
# INCORRECTO — blacklist (frágil)
data.pop('slug', None)
for key, value in data.items():
setattr(vendor, key, value)
# INCORRECTO — pass-through directo
Model.objects.create(**payload.dict())
6.2 Defensa en Profundidad
Aunque el Schema de Ninja filtra campos, siempre hacer data.pop('id', None) antes de create(**data) como defensa adicional.
7. CSRF Protection
- Django CSRF middleware: Habilitado globalmente
- SameSite=Strict: Cookie de sesión no se envía cross-site
- ApiService.js: Todas las peticiones frontend usan CSRF token automáticamente
@csrf_exempt: Solo en health probes (/health,/ready,/alive) — endpoints de solo lectura sin estado
8. XSS Prevention
- Django templates: Auto-escape habilitado por defecto
|safefilter: Solo usado en datos JSON server-generated (json.dumps()), nunca en user inputmark_safe(): No usado en el codebase- CSP:
script-src 'self' 'nonce-{random}'— only nonce’d<script>blocks execute. Nounsafe-inline, nounsafe-eval - Delegated event handlers: All
onclick/onchange/etc. converted todata-actionattributes with global event delegation inbase.js
9. SQL Injection Prevention
- ORM exclusivo: Todas las queries usan Django ORM
- Sin raw queries: El único
cursor.execute("SELECT 1")es el health check (hardcoded) - Parametrización: Django ORM parametriza automáticamente
10. Infraestructura Docker
10.1 Valkey (Cache)
# compose.prod.yml
cache:
command: >
valkey-server
--requirepass ${REDIS_PASSWORD}
--maxmemory 512mb
--maxmemory-policy allkeys-lru
Password: Generada con openssl rand -hex 32 (solo 0-9a-f). NUNCA usar base64 — los caracteres /+= rompen URLs de Redis.
10.2 PostgreSQL
Solo accesible dentro de la red Docker interna. Sin puerto expuesto al host.
10.3 Django SECRET_KEY
# config/settings/production.py
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "")
if not SECRET_KEY or "insecure" in SECRET_KEY:
raise ImproperlyConfigured(
"DJANGO_SECRET_KEY must be set to a secure random value in production"
)
11. VibeSec Pro (AI Security Skill)
Ubicación: .claude/skills/SKILL.md (2,401 líneas, excluido de git)
Archivo de instrucciones que enseña a los agentes IA (Claude, Cursor, Copilot) a escribir código seguro por defecto. Cubre:
- 30 tipos de vulnerabilidades: IDOR, XSS, CSRF, SSRF, SQLi, XXE, JWT, path traversal, file upload, open redirect, mass assignment, secret exposure, NoSQL injection, postMessage, etc.
- 140 técnicas de bypass: Tablas detalladas de evasión para cada tipo
- Framework references: Flask/Python (relevante para Django), Express, Next.js, React+Vite, Supabase
Licencia: Comercial (lifetime). No se sube al repositorio público.
12. Auditorías Realizadas
11-03-2026 — VibeSec Audit (10 hallazgos)
| # | Tipo | Severidad | Hallazgo | Remediación |
|---|---|---|---|---|
| 1 | SSRF | Crítico | Webhooks ITSM sin validación de URL | validate_webhook_url() + IP blocking + no redirects |
| 2 | IDOR | Crítico | Device details sin filtro org | rack__organization=org en query |
| 3 | IDOR | Crítico | Device status sin filtro org | rack__organization=org en query |
| 4 | Mass Assignment | Alto | VendorProfile.create(**dict()) | data.pop('id', None) |
| 5 | Mass Assignment | Alto | Setattr con blacklist | Whitelist UPDATABLE_FIELDS |
| 6 | IDOR | Alto | Stencils con post-lookup check | Pre-filtro con Q(org) | Q(null) |
| 7 | SSRF | Alto | Redirects habilitados en webhooks | allow_redirects=False |
| 8 | Logic | Medio | get_current_org() fallback a first org | Retorna None |
| 9 | Info Leak | Medio | Error interno en test webhook | Mensaje genérico + log |
| 10 | DoS | Medio | Timeout 10s en webhooks | Reducido a 3s |
11-03-2026 — Security Hardening (12 remediaciones)
| # | Severidad | Hallazgo | Remediación |
|---|---|---|---|
| 1 | Crítico | API key en .env | Confirmado falso positivo (no en git) |
| 2 | Crítico | /diag/oids sin auth | JWT obligatorio |
| 3 | Crítico | SECRET_KEY no validado | ImproperlyConfigured si falta |
| 4 | Crítico | /metrics público | Middleware IP restriction |
| 5 | Alto | CSP unsafe-eval | ✅ Eliminado — Alpine CSP build + nonces |
| 6 | Alto | CSP localhost | ✅ Path-based — solo en Agent pages |
| 7 | Alto | /health leak errores | Sanitizado + logger |
| 8 | Alto | Rate limit IP spoofing | Trusted proxies pattern |
| 9 | Alto | Valkey sin password | --requirepass + env var |
| 10 | Medio | Sin Permissions-Policy | Header añadido en CSP middleware |
| 11 | Medio | SameSite=Lax | Actualizado a Strict |
| 12 | Medio | ALLOWED_HOSTS ["*"] en dev | Restringido a localhost |
13. Pruebas de Seguridad (External Scan)
13.1 Metodología
Scan externo desde fuera de la red (misma perspectiva que un atacante) contra https://crearack.com. Equivalente a lo que ejecutan herramientas como Nikto, OWASP ZAP, Nuclei y SimpleSecCheck.
Categorías probadas:
- HTTP Security Headers (8 headers)
- TLS/SSL (versiones, certificado)
- Rutas sensibles (9 paths comunes)
- Host header injection
- CORS misconfiguration
- HTTP methods (OPTIONS, TRACE, PUT)
- Rate limiting (decremento verificado)
- Error pages (info leakage)
- Endpoints internos (/health, /metrics, /diag/oids)
13.2 Resultados (11-03-2026)
Headers HTTP — 8/8 presentes
| Header | Valor | Estado |
|---|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains; preload | PASS |
X-Frame-Options | DENY | PASS |
X-Content-Type-Options | nosniff | PASS |
Content-Security-Policy | 11 directivas | PASS |
Referrer-Policy | strict-origin-when-cross-origin | PASS |
Permissions-Policy | 8 APIs bloqueadas | PASS |
Cross-Origin-Opener-Policy | same-origin | PASS |
X-Ratelimit-* | 300 req/ventana | PASS |
TLS/SSL
| Check | Resultado |
|---|---|
| TLS 1.3 | Soportado |
| TLS 1.2 | Soportado |
| TLS 1.1 / 1.0 | Bloqueado (Cloudflare upgrade a 1.3) |
| Certificado | Let’s Encrypt R13, válido 3 meses |
Rutas sensibles — Todas bloqueadas
/.env → 404 /.git/config → 404
/wp-admin → 404 /debug → 404
/server-status → 404 /phpinfo.php → 404
/robots.txt → 404 /sitemap.xml → 404
/.well-known/security.txt → 404
/metrics → 403 Forbidden (IP restriction)
/api/diag/oids → 404 (requires JWT)
Protecciones activas
| Test | Comando | Resultado |
|---|---|---|
| Host injection | curl -H "Host: evil.com" | 404 (Traefik bloquea) |
| CORS reflection | curl -H "Origin: https://evil.com" | Sin Access-Control headers |
| HTTP PUT | curl -X PUT / | 403 CSRF |
| HTTP TRACE | curl -X TRACE / | 302 redirect (no ejecuta) |
| Error page leak | curl /nonexistent-page | Solo “Not Found” (sin stack trace) |
| Admin exposure | curl /admin/ | 302 → login (no expone panel) |
| Rate limiting | 5 requests consecutivos | 300→294 (decrementa correctamente) |
Endpoint /health (corregido 11-03-2026)
# ANTES (información sensible expuesta):
# {"status":"healthy","version":"1.0.33",
# "checks":{"database":{"status":"healthy","type":"postgresql"},
# "cache":{"status":"healthy","type":"valkey"}},
# "response_time_ms":18.1}
# DESPUÉS (sanitizado):
# {"status":"healthy","checks":{"database":"ok","cache":"ok"}}
Eliminado: versión, tipo de base de datos, tipo de cache, tiempo de respuesta.
13.3 Mozilla Observatory (11-03-2026) — Score: 125/100 (A+)
Scan via API: https://observatory-api.mdn.mozilla.net/api/v2/analyze?host=crearack.com
| # | Test | Estado | Puntos | Detalle |
|---|---|---|---|---|
| 1 | Cookies | PASS | +5 | Secure + HttpOnly + SameSite |
| 2 | CORS | PASS | 0 | No implementado (correcto) |
| 3 | Redirection | PASS | 0 | HTTP → HTTPS correcto |
| 4 | Referrer-Policy | PASS | +5 | strict-origin-when-cross-origin |
| 5 | HSTS | PASS | 0 | 1 año + includeSubDomains + preload |
| 6 | Subresource Integrity | N/A | 0 | Scripts from same origin |
| 7 | X-Content-Type-Options | PASS | 0 | nosniff |
| 8 | X-Frame-Options | PASS | +5 | DENY via CSP frame-ancestors |
| 9 | Cross-Origin Resource Policy | N/A | 0 | No implementado |
| 10 | Content-Security-Policy | PASS | 0 | Nonce-based, default-src 'none', no unsafe-inline in script-src |
Cálculo: 100 (base) + 10 (no unsafe-inline in style-src) + 15 (bonus, base ≥ 90) = 125 (A+)
CSP Migration (completada 11-03-2026): 7 fases — Alpine CSP build, nonces en 25 scripts, ~230 inline handlers convertidos a data-action, default-src 'none', path-based CSP para Agent pages. Ver Documentation/archive/plans/CSP_MIGRATION_PLAN.md.
13.4 Hallazgos abiertos (trade-offs aceptados)
| # | Hallazgo | Severidad | Motivo |
|---|---|---|---|
| 1 | style-src-elem/attr 'unsafe-inline' | Baja | Requerido por HTMX (<style> dinámicos), ECharts/Konva (atributos style=""). No permite ejecución de código. Granular CSP L3 — Observatory no lo detecta (125/100) |
| 2 | Server header daphne | Baja | Información menor, Daphne es estándar |
| 3 | HSTS preload no registrado | Baja | Directive presente pero no enviado a hstspreload.org |
13.5 Cómo reproducir el scan
# 0. Mozilla Observatory (API directa — resultado JSON completo)
curl -s "https://observatory-api.mdn.mozilla.net/api/v2/analyze?host=crearack.com"
# 1. Headers HTTP (todos los headers de seguridad)
curl -sI https://crearack.com
# 2. TLS versions
for ver in tls1 tls1_1 tls1_2 tls1_3; do
echo | openssl s_client -connect crearack.com:443 -$ver 2>&1 | grep "Protocol"
done
# 3. Rutas sensibles
for path in ".env" ".git/config" "wp-admin" "debug" "server-status" "metrics"; do
echo "$path → $(curl -sI -o /dev/null -w '%{http_code}' https://crearack.com/$path)"
done
# 4. Host injection
curl -sI https://crearack.com -H "Host: evil.com"
# 5. CORS
curl -sI https://crearack.com -H "Origin: https://evil.com" | grep "access-control"
# 6. HTTP methods
curl -sI -X PUT https://crearack.com/
curl -sI -X TRACE https://crearack.com/
# 7. Rate limiting
for i in $(seq 1 5); do
curl -sI https://crearack.com/login | grep "x-ratelimit-remaining"
done
# 8. Info leak en /health
curl -s https://crearack.com/health
# 9. Error pages
curl -s https://crearack.com/nonexistent-12345
13.6 Herramientas recomendadas
| Herramienta | Tipo | Uso |
|---|---|---|
curl + scripts | CLI | Scan rápido de headers, rutas, métodos |
| Mozilla Observatory | Online/API gratis | 10 tests HTTP, scoring oficial. API: observatory-api.mdn.mozilla.net/api/v2/analyze?host= |
| testssl.sh | CLI | Análisis TLS/SSL exhaustivo |
| Nuclei | CLI | Templates de vulnerabilidades conocidas |
| SimpleSecCheck | Docker | 31 scanners integrados (build pesado, Node 18 deprecado) |
Nota: Estas herramientas comprueban la superficie externa. No detectan IDOR, SSRF, mass assignment ni lógica de negocio — eso requiere auditoría manual del código (ver sección 12).
14. Checklist para Nuevos Endpoints
Al crear un nuevo endpoint API, verificar:
- Filtrar por
organization=orgen todos los queries con ID - Validar permisos (admin, operator, viewer)
- No aceptar
**payload.dict()sin filtrar campos peligrosos - No exponer errores internos al cliente (
str(e)→ mensaje genérico) - Si acepta URLs del usuario → validar con
validate_webhook_url() - Si modifica estado → verificar CSRF protection
- Si es solo lectura sin auth → justificar
@csrf_exempt - Rate limiting en endpoints sensibles (login, AI, webhook)
15. Passwords y Secretos
Generación
# Para Redis/Valkey URLs (solo hex, sin caracteres especiales)
openssl rand -hex 32
# Para Django SECRET_KEY
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
NUNCA usar openssl rand -base64 para passwords que se embeben en URLs — los caracteres /+= rompen el parser de URLs de Python.
Almacenamiento
| Secreto | Dónde | Cómo |
|---|---|---|
| DJANGO_SECRET_KEY | Dokploy env vars | Variable de entorno |
| REDIS_PASSWORD | Dokploy env vars | Variable de entorno |
| GEMINI_API_KEY | Dokploy env vars | Variable de entorno |
| Agent JWT tokens | Windows DPAPI | Cifrado en disco del usuario |
Rotación
| Secreto | Frecuencia |
|---|---|
| DJANGO_SECRET_KEY | Anual o tras incidente |
| REDIS_PASSWORD | Anual o tras incidente |
| GEMINI_API_KEY | Trimestral |
| Agent access tokens | 72 horas (automático) |
16. Respuesta a Incidentes
- Revocar acceso: Cambiar passwords comprometidas en Dokploy env vars
- Redeploy: Push vacío o redeploy manual en Dokploy
- Verificar: Ejecutar checklist de Fase 5 (ver
archive/plans/SECURITY_HARDENING_PLAN.md) - Auditar logs:
docker compose logs web | grep ERROR - Documentar: Actualizar esta guía con lecciones aprendidas
Referencias
- SECURITY_HARDENING_PLAN.md — Plan completo de hardening con estado de implementación
- SECURITY_AUDIT.md — Auditoría de dependencias y licencias
- Django Security Checklist
- OWASP Top 10 (2021)
- VibeSec — Security skill para agentes IA
Mantenido por: Equipo CreaRack + Claude (Anthropic)
Véase también
- [[crearack-tech—reports—security-audit]] — auditoría de seguridad y licencias
- [[crearack-tech—reports—security-audit-04-04-2026]] — auditoría de seguridad crearack.com
- [[crearack-tech—guides—secret-rotation-playbook]] — playbook de rotación de secretos
- [[crearack-tech—backend—passkeys-authentication]] — autenticación con passkeys
- [[crearack-tech—agents—dev-core]] — perfil de subagente dev-core
- [[workspace-tech—tecnico—security-audit-11-04-2026]] — auditoría seguridad workspace