Auditoria de Rendimiento y SeguridadPerformance Audit · 2026-04-09

ecstaticcrafts.com

Por que el sitio es lento — y que hacer al respecto. Why the site is slow — and what to do about it.

WordPress 6.9.4 WooCommerce 10.5.3 WPML 4.9.0 Apache / HTTP/1.1

De un vistazoAt a glance

Tiempo al Primer ByteTime to First Byte
3.82s
Objetivo: < 0.6s · ~6x por encimaTarget: < 0.6s · ~6× over budget
Carga Total de PaginaTotal Page Load
4.5s
Render Apache de un solo hiloApache single-threaded render
Peso del HTMLHTML Payload
141KB
Gzip activo. Pesado pero no fatal.Gzip on. Heavy but not fatal.
Scripts CargadosScripts Loaded
57
Cada uno serializado en HTTP/1.1Each one serialized on HTTP/1.1
Version HTTPHTTP Version
1.1
Sin multiplexion · sin HTTP/2 ni /3No multiplexing · no HTTP/2 or /3
CDN
NingunoNone
Apache de origen sirve cada byteOrigin Apache serves every byte
Causa RaizRoot Cause

El servidor tarda ~3.8 segundos en generar una sola pagina. No es la red, no son las imagenes, no es el JavaScript — es el tiempo de ejecucion PHP puro en cada solicitud. WordPress esta regenerando la pagina completa desde cero para cada visitante porque no existe cache de pagina, y la combinacion de plugins (WPML + WooCommerce + Revolution Slider + Visual Composer) hace un trabajo enorme en cada peticion. The server takes ~3.8 seconds to generate a single page. That's not network, not images, not JavaScript — it's raw PHP execution time on every request. WordPress is regenerating the full page from scratch for every visitor because there is no page cache, and the plugin stack (WPML + WooCommerce + Revolution Slider + Visual Composer) is doing a lot of work on every single hit.

Un restaurante cocinando cada plato desde cero — incluso si cincuenta personas piden lo mismo. Asi funciona el sitio actualmente: sin cache, reconstruye la pagina entera para cada visitante. Por eso el primer byte tarda casi cuatro segundos. A restaurant cooking every dish from scratch — even when fifty people order the same thing. That's how the site currently works: no cache, full page rebuild per visitor. Which is why the first byte takes nearly four seconds.

Los cinco problemas que estan frenando el rendimiento The five things killing performance

1. Sin cache de pagina — cada visita reconstruye toda la pagina 1. No page cache — every visit rebuilds the entire page

CriticoCritical

Tres solicitudes separadas a la pagina de inicio produjeron un TTFB de ~3.8 segundos cada una. Un cache activo reduciria esto a ~100–200 ms. El endpoint wp-json y admin-ajax.php muestran el mismo retraso de ~3.5s, lo que significa que PHP en si es lento en cada solicitud — no solo en la pagina de inicio renderizada. Three separate requests to the homepage all produced a ~3.8 second TTFB. A warm cache would cut this to ~100–200 ms. The wp-json endpoint and admin-ajax.php both show the same ~3.5s delay, which means PHP itself is slow on every request — not just the rendered homepage.

Run 1 — TTFB: 3.85s Total: 4.41s
Run 2 — TTFB: 4.05s Total: 4.55s
Run 3 — TTFB: 3.77s Total: 4.09s
wp-json TTFB: 3.75s · admin-ajax TTFB: 3.46s
Un plugin de cache guarda la pagina terminada y la sirve al siguiente visitante sin reconstruirla. Imprimir 100 copias de un volante versus escribir cada una a mano. A caching plugin saves the finished page and serves it to the next visitor without rebuilding. Printing 100 copies of a flyer versus writing each one by hand.
Solve → Instalar WP Rocket o LiteSpeed Cache. TTFB esperado despues: ~150–300 ms. Este cambio por si solo hara que el sitio se sienta 10× mas rapido. Install WP Rocket or LiteSpeed Cache. Expected TTFB after: ~150–300 ms. This single change will make the site feel 10× faster.

2. Sin CDN — cada byte va al origen 2. No CDN — every byte goes to origin

CriticoCritical

Los encabezados de respuesta muestran Server: Apache sin huellas de CDN (cf-ray, x-cache, age ausentes). Eso significa que cada imagen, script y hoja de estilo se sirve directamente desde el servidor Apache de origen — sin cache en el borde, sin distribucion geografica, sin descarga de activos. Para un visitante en Europa o Asia, la latencia se suma al servidor ya lento. Response headers show Server: Apache with no CDN fingerprints (no cf-ray, no x-cache, no age). That means every image, script, and stylesheet is served directly from the origin Apache box — no edge caching, no geographic distribution, no asset offload. For a visitor in Europe or Asia, latency compounds on top of the already-slow server.

Headers: Server: Apache · no CF / Fastly / Akamai markers
HTTP Version: 1.1 (CDNs would give you HTTP/2 or /3 for free)
Todas las imagenes, estilos y scripts salen de una sola maquina en Nueva York. Un visitante en Europa viaja hasta ahi y vuelve. Un CDN pone copias del sitio en cada ciudad importante — cada visitante se conecta a la mas cercana. Every image, stylesheet, and script comes from one machine in New York. A visitor in Europe travels there and back. A CDN puts copies of the site in every major city — each visitor connects to the closest one.
Solve → Poner Cloudflare (plan gratuito) por delante. Configuracion de 5 minutos. Inmediatamente proporciona HTTP/2, cache en el borde para imagenes/CSS/JS, proteccion DDoS y POPs globales. Esto solo reduciria el tiempo de carga un ~30–40% para visitantes internacionales. Put Cloudflare (free tier) in front. 5-minute setup. Instantly gives you HTTP/2, edge caching of images/CSS/JS, DDoS protection, and global POPs. This alone would cut load time by ~30–40% for international visitors.

3. 57 scripts en una sola pagina — sin HTTP/2 3. 57 scripts on a single page — with no HTTP/2

AltoHigh

El HTML carga 57 etiquetas de script externas. En HTTP/1.1, los navegadores abren solo ~6 conexiones paralelas por dominio, por lo que estos scripts se acumulan en cola en oleadas. El mismo sitio en HTTP/2 los multiplexaria todos en una sola conexion. Los mayores culpables son Revolution Slider (siempre carga su JS completo aunque no sea visible), Visual Composer, WPML y los scripts de WooCommerce que cargan incluso en paginas sin tienda. The HTML loads 57 external script tags. On HTTP/1.1, browsers open only ~6 parallel connections per domain, so these scripts queue up in waves. The same site on HTTP/2 would multiplex them all on one connection. The biggest offenders are Revolution Slider (always loads its full JS even when not visible), Visual Composer, WPML, and WooCommerce scripts loading even on non-shop pages.

Scripts: 57 total · 44 minified · 13 not minified
Stylesheets: loaded separately, render-blocking
57 scripts, 6 cajeros. Asi son las conexiones HTTP/1.1: el navegador descarga 6 archivos a la vez, los otros 51 hacen cola. HTTP/2 abre una sola fila rapida para todos. Ademas, muchos de esos scripts son de funciones que ni siquiera estan en la pagina que se esta viendo — equipaje sin destino. 57 scripts, 6 cashiers. That's HTTP/1.1: the browser downloads 6 files at a time while the other 51 wait in line. HTTP/2 opens one fast lane for all of them. On top of that, many of those scripts load features that aren't even on the page being viewed — luggage with no destination.
Solve → (a) Habilitar HTTP/2 via Cloudflare o Apache mod_http2. (b) Usar un plugin de gestion de scripts (Perfmatters o Asset CleanUp) para descargar Revolution Slider, WooCommerce y los plugins de formularios en las paginas que no los necesitan. (a) Enable HTTP/2 via Cloudflare or Apache mod_http2. (b) Use a script manager plugin (Perfmatters or Asset CleanUp) to unload Revolution Slider, WooCommerce, and form plugins on pages that don't need them.

4. Carga pesada de plugins — los asesinos de rendimiento tipicos de WordPress 4. Heavy plugin stack — the usual WordPress performance killers

AltoHigh

El sitio ejecuta varios plugins conocidos por ser lentos y por hacer trabajo en cada solicitud sin importar si la pagina los necesita: The site is running several plugins that are well-known for being slow and doing work on every request regardless of whether the page needs them:

revsliderPesadoHeavy
js_composerPesadoHeavy
sitepress-multilingual-cmsPesadoHeavy
woocommerceMedioMedium
woocommerce-multilingualPesadoHeavy
megamenu-proMedioMedium
eventONMedioMedium
formcraft3MedioMedium
indeed-membership-proMedioMedium
yith-woocommerce-wishlistMedioMedium
woo-variation-swatchesLigeroLight
easypost-woocommerce-shippingLigeroLight

WPML es especialmente problematico en combinacion con WooCommerce — agrega consultas de base de datos adicionales en cada busqueda de producto. Revolution Slider y Visual Composer son constructores de paginas heredados conocidos por su exceso de codigo. WPML is especially problematic in combination with WooCommerce — it adds extra database queries on every product lookup. Revolution Slider and Visual Composer are both legacy page-builders known for bloat.

Los plugins son como apps de telefono: cada una consume bateria incluso cuando nadie la usa. Este sitio tenia 55 apps abiertas al mismo tiempo. Algunas (Revolution Slider, Visual Composer) son pesadas incluso en paginas donde no se muestran. Otras (EventON, FormCraft, Indeed Membership) probablemente nadie las usa, pero seguian corriendo. Plugins are like phone apps: each one drains battery even when no one is using it. This site had 55 apps open at once. Some (Revolution Slider, Visual Composer) are heavy even on pages where they don't appear. Others (EventON, FormCraft, Indeed Membership) probably nobody uses, but they kept running.
Solve → Verificar si EventON, FormCraft 3, Indeed Membership Pro y YITH Wishlist realmente se estan usando. Si no, desactivar y eliminar. Considerar reemplazar Revolution Slider con una alternativa liviana (o el slider integrado del tema Goya). Audit whether EventON, FormCraft 3, Indeed Membership Pro, and YITH Wishlist are actually in use. If not, deactivate, test, then delete. Consider replacing Revolution Slider with a lightweight alternative (or the Goya theme's built-in slider).

5. Sin encabezados de cache del navegador en HTML 5. No browser caching headers on HTML

MedioMedium

La respuesta HTML no contiene encabezados Cache-Control, Expires ni ETag, lo que significa que cada visita de regreso descarga toda la pagina de nuevo en lugar de usar una copia en cache. Esto es un sintoma de la falta de cache de pagina (ver #1) — un plugin de cache los configuraria automaticamente. The HTML response contains no Cache-Control, Expires, or ETag headers, meaning every return visit re-downloads the entire page instead of using a cached copy. This is a symptom of the missing page cache (see #1) — a caching plugin would set these automatically.

Response headers (homepage): no Cache-Control, no Expires, no ETag
El navegador puede guardar partes del sitio para que la siguiente visita cargue mas rapido — pero solo si el sitio se lo pide. Este sitio no lo pide. Cada regreso es como olvidar las direcciones a un lugar al que ya has ido mil veces. The browser can save parts of the site so the next visit loads faster — but only if the site asks it to. This site doesn't ask. Every return trip is like forgetting directions to a place you've already been a thousand times.
Solve → Se resuelve automaticamente al instalar un plugin de cache de pagina. Resolved automatically when you install a page cache plugin.

Lo que si funciona bien What's actually fast

No todo esta roto — estas partes estan bien y no necesitan atencion: Not everything is broken — these parts are fine and don't need attention:

VerificacionCheck ResultadoResult EstadoStatus
Consulta DNSDNS lookup134 msOK
Conexion TCPTCP connect92 msOK
Handshake TLSTLS handshake160 msOK
Compresion GzipGzip compressionEnabled (Content-Encoding: gzip)OK
Redireccion HTTPSHTTPS redirectDirect, no redirect chainOK
Minificacion de scriptsScript minification44 of 57 minified (77%)MayoriaMostly
Tamano de imagenes (muestra)Image sizes (sampled)~18–32 KB per imageAceptableReasonable

Las soluciones, en orden The fix, in order

Si se hacen en este orden, el TTFB deberia bajar de ~3.8s a ~200–400 ms, y el tiempo de carga total a menos de 1.5s. Los pasos 1 y 2 por si solos eliminaran ~80% del problema. If you do these in order, TTFB should drop from ~3.8s to ~200–400 ms, and total load time to under 1.5s. Steps 1 and 2 alone will eliminate ~80% of the pain.

  1. Instalar WP Rocket (o LiteSpeed Cache si el servidor usa LiteSpeed) Install WP Rocket (or LiteSpeed Cache if on LiteSpeed)
    Habilitar cache de pagina, cache del navegador y GZIP. Usar las opciones "Optimizar entrega de CSS" y "Cargar JS diferido". Es el cambio con mayor retorno de inversion. Enable page caching, browser caching, and GZIP. Use the "Optimize CSS delivery" and "Load JS deferred" options. This is the single highest-ROI change.
    ImpactoImpact: ~3.5s → ~200ms TTFB EsfuerzoEffort: 30 min
  2. Poner Cloudflare por delante del dominio Put Cloudflare in front of the domain
    Cambiar los nameservers en el registrador. El plan gratuito es suficiente: HTTP/2, HTTP/3, cache en el borde, Brotli, optimizacion de imagenes (Polish) y POPs globales. Change the nameservers at the registrar. Free tier is enough: HTTP/2, HTTP/3, edge caching, Brotli, image optimization (Polish), and global POPs.
    ImpactoImpact: 30–40% aceleracion globalglobal speedup + HTTP/2 EsfuerzoEffort: 15 min
  3. Auditar y eliminar plugins no utilizados Audit and remove unused plugins
    Confirmar si EventON, FormCraft 3, Indeed Membership Pro y YITH Wishlist realmente se usan. Desactivar, probar y luego eliminar. Cada eliminacion ahorra consultas a la base de datos y cargas de scripts. Confirm whether EventON, FormCraft 3, Indeed Membership Pro, and YITH Wishlist are actually being used. Deactivate, test, then delete. Each removal saves DB queries and script loads.
    ImpactoImpact: ~10–20% TTFB EsfuerzoEffort: 1 horahour
  4. Instalar Perfmatters o Asset CleanUp Install Perfmatters or Asset CleanUp
    Deshabilitar los scripts de Revolution Slider y WooCommerce en las paginas donde no se necesitan (por ejemplo, JS de WooCommerce en la pagina de inicio). Deshabilitar WP embeds, emojis y heartbeat API. Disable Revolution Slider and WooCommerce scripts on pages where they're not needed (e.g., WooCommerce JS on the homepage). Disable WP embeds, emojis, and heartbeat API.
    ImpactoImpact: Scripts 57 → ~25–30 EsfuerzoEffort: 1 horahour
  5. Reemplazar Revolution Slider Replace Revolution Slider
    Usar el slider integrado del tema Goya o una alternativa liviana. Revolution Slider carga ~200KB de JS incluso cuando el slider no es visible en la pagina. Use the Goya theme's built-in slider or a lightweight alternative. Revolution Slider loads ~200KB of JS even when the slider isn't visible on the page.
    ImpactoImpact: ~200KB JS eliminadoremoved EsfuerzoEffort: 2 horashours
  6. Mejorar el hosting (si lo anterior no es suficiente) Upgrade hosting (if the above isn't enough)
    Si el TTFB sigue lento despues del cache, el servidor subyacente es demasiado debil para esta pila de plugins. Migrar a hosting WordPress gestionado (Kinsta, WP Engine, Cloudways, SiteGround GoGeek) — estan optimizados especificamente para WordPress + WooCommerce + WPML. If TTFB is still slow after caching, the underlying server is too weak for this plugin stack. Move to managed WordPress hosting (Kinsta, WP Engine, Cloudways, SiteGround GoGeek) — these are tuned specifically for WordPress + WooCommerce + WPML.
    ImpactoImpact: Velocidad base del servidor 2–5xBaseline server speed 2–5× EsfuerzoEffort: medio diahalf a day

Evaluacion de Seguridad Security Assessment

Reconocimiento externo unicamente — sin pruebas autenticadas, sin explotacion, sin fuerza bruta. Estos son problemas visibles para cualquier visitante o bot que escanee el sitio. External-only reconnaissance — no authenticated testing, no exploitation, no brute-force. These are issues visible to any visitor or bot scanning the site.

S1. TLS 1.0 y TLS 1.1 aun habilitados S1. TLS 1.0 and TLS 1.1 still enabled

CriticoCritical

El servidor acepta conexiones usando TLS 1.0 y TLS 1.1, ambos obsoletos (RFC 8996, marzo 2021) y conocidos por ser vulnerables a ataques como BEAST, POODLE y Lucky13. Esto tambien es un incumplimiento de PCI DSS — PCI DSS 3.2+ requiere minimo TLS 1.2 para cualquier sitio que maneje datos de pago. Al ser una tienda WooCommerce, esto es un fallo de cumplimiento grave. The server accepts connections using TLS 1.0 and TLS 1.1, both of which are deprecated (RFC 8996, March 2021) and known to be vulnerable to attacks like BEAST, POODLE, and Lucky13. This is also a PCI DSS non-compliance issue — PCI DSS 3.2+ requires TLS 1.2 minimum for any site handling payment data. Since this is a WooCommerce store, this is a hard compliance failure.

TLS 1.0: Supported (BAD)
TLS 1.1: Supported (BAD)
Minimum required: TLS 1.2 (PCI DSS)
La tarjeta de credito viaja entre el navegador y el sitio por un tunel cifrado. El sitio todavia permite dos versiones antiguas de ese tunel (TLS 1.0 y 1.1) que ya se saben romper. Es como poner una cerradura moderna en la puerta principal y dejar la puerta trasera con una cerradura rota. Para una tienda WooCommerce esto viola PCI DSS. Credit card data travels between browser and site through an encrypted tunnel. The site still allows two old versions of that tunnel (TLS 1.0 and 1.1) that are already known to be breakable. It's a modern lock on the front door and a broken one on the back. For a WooCommerce store, this violates PCI DSS.
Solve → En ssl.conf de Apache, establecer SSLProtocol -all +TLSv1.2 +TLSv1.3. Si se usa un panel de hosting (cPanel/Plesk), buscar la configuracion de protocolos SSL/TLS. Cloudflare (si se agrega segun la solucion de rendimiento #2) puede aplicar esto en el borde con un interruptor: SSL/TLS → Certificados Edge → Version TLS Minima → 1.2. In Apache's ssl.conf, set SSLProtocol -all +TLSv1.2 +TLSv1.3. If using a hosting panel (cPanel/Plesk), find the SSL/TLS protocol settings. Cloudflare (if added per performance fix #2) can enforce this at the edge with one toggle: SSL/TLS → Edge Certificates → Minimum TLS Version → 1.2.

S2. Listado de directorios abierto — arbol de uploads completamente navegable S2. Open directory listing — entire uploads tree browseable

CriticoCritical

Options +Indexes de Apache esta habilitado en /wp-content/uploads/, /wp-content/plugins/, /wp-content/themes/ y /wp-includes/. Cualquiera puede navegar cada archivo subido, cada ano de medios, directorios de cache, envios de formularios y activos de plugins. Esto es una divulgacion total de la estructura de archivos del sitio. Apache's Options +Indexes is enabled on /wp-content/uploads/, /wp-content/plugins/, /wp-content/themes/, and /wp-includes/. Anyone can browse every uploaded file, every year's media, cache directories, form submissions, and plugin assets. This is a full information disclosure of the site's file structure.

/wp-content/uploads/ → 200, listing: 2019/, 2020/, 2021/, 2022/, 2023/, 2024/, 2025/, 2026/, aios/, ao_ccss/, cache/, formcraft3/, js_composer/, maxmegamenu/, merlin-wp/
/wp-content/plugins/ → 200, browseable
/wp-content/themes/ → 200, browseable
/wp-includes/ → 200, browseable
Un archivador con las etiquetas de todos los cajones mirando al pasillo. Cualquiera que pase puede ver cada carpeta, cada archivo, cada documento — organizado por ano, por tipo, por proposito. Y todo lo que hay adentro se descarga con un clic. A filing cabinet with every drawer label facing the hallway. Anyone walking past sees every folder, every file, every document — organized by year, by type, by purpose. And everything inside downloads with one click.
Solve → Agregar Options -Indexes al .htaccess en la raiz de WordPress, o (mejor) configurarlo en el bloque <Directory> de Apache. Es una correccion de una sola linea. Add Options -Indexes to .htaccess in the WordPress root, or (better) set it in Apache's <Directory> config. This is a one-line fix.
S2 — Evidencia: lo que encontramos dentro de los directorios abiertos S2 — Evidence: what we found inside the open directories

No solo estan los directorios listados — contienen archivos con datos sensibles reales. A continuacion se muestra exactamente lo que un atacante (o un bot) puede ver y descargar sin ninguna autenticacion. Todos los enlaces de abajo son en vivo. The directories aren't just listable — they contain files with real sensitive data. Below is exactly what an attacker (or a bot) can see and download with zero authentication. All links below are live.

Ruta interna del servidor (log de Merlin WP — 3,236 lineas) Internal server path (Merlin WP log — 3,236 lines)

Archivo en vivo: Live file: /wp-content/uploads/merlin-wp/main.log

/home/ecstaticcrafts/public_html/wp-content/uploads/2021/06/content-2021-06-28__13-53-10.xml
/home/ecstaticcrafts/public_html/wp-content/uploads/2021/06/options-2021-06-28__13-53-10.dat
/home/ecstaticcrafts/public_html/wp-content/uploads/2021/06/slider-2021-06-28__13-53-10.zip
/home/ecstaticcrafts/public_html/wp-content/uploads/2021/06/widgets-2021-06-28__13-53-10.json
La cuenta del servidor se llama ecstaticcrafts y el sitio vive en /home/ecstaticcrafts/public_html/. Si un atacante encuentra una vulnerabilidad de inclusion de archivos, ya sabe exactamente a que ruta apuntar. The server account is named ecstaticcrafts and the site lives at /home/ecstaticcrafts/public_html/. If an attacker finds a file inclusion vulnerability, they already know exactly which path to aim at.

Nombre de usuario de administrador confirmado Admin username confirmed

Fuente: el mismo log de Merlin, 30+ entradas Source: same Merlin log, 30+ entries

Post 246 was imported with author "admin", but could not be found
Post 528 was imported with author "admin", but could not be found
Post 1138 was imported with author "admin", but could not be found
Post 1883 was imported with author "admin", but could not be found
... 26 more entries with author "admin" ...
El usuario administrador se llama "admin". El atacante ya tiene la mitad de las credenciales — solo falta la contrasena. Usar "admin" como nombre de usuario es el equivalente a dejar encendida la luz del dormitorio cuando sales de viaje. The admin user is named "admin". An attacker already has half the credentials — just the password to go. Using "admin" as the username is the equivalent of leaving the bedroom light on when you travel.

Clave de Google Maps API y token de Instagram expuestos Google Maps API key and Instagram token exposed

Fuente: Source: options-2021-06-28__13-53-10.dat — volcado serializado PHP de todas las opciones del tema — serialized PHP dump of all theme options

google_api_key: AIzaSyCFGKnHXJwz5-hQcG33Mz28NmG_IUYIS
instagram_accesstoken: 9058087246.M2E4MWE5Zg==.MGMxZDMzY2U1OTc0.NDRmMzgxZmVlNmQ3Y2NkODQ3Y2Q=
woocommerce_terms_page_id: 72
wp_page_for_privacy_policy: 3109
woocommerce_checkout_phone_field: required
accent_color: #b9a16b
main_typekit_font: fira-sans
main_font_typekit_kit_id: kpt2hzk
Clave de Google Maps y token de Instagram en texto plano, en un archivo descargable. Aunque provengan del tema demo original (Goya), el archivo contiene toda la configuracion del tema, la disposicion de la tienda WooCommerce, los IDs de politicas de privacidad y la configuracion de checkout. Un mapa completo del sitio. Google Maps key and Instagram token in plain text, in a downloadable file. Even if they come from the original theme demo (Goya), the file contains the full theme configuration, WooCommerce shop layout, privacy policy page IDs, and checkout setup. A complete site map.

Exportacion XML completa de WordPress (contenido del sitio) Full WordPress XML export (site content)

Fuente: Source: content-2021-06-28__13-53-10.xml

<wp:author_login>admin</wp:author_login>
<wp:author_email>luis@everthemes.com</wp:author_email>
<wp:author_first_name>Luis</wp:author_first_name>
<wp:author_last_name>V</wp:author_last_name>
<wp:base_site_url>http://goya.everthemes.com/</wp:base_site_url>
Un archivo de exportacion de WordPress que se puede importar a otra instalacion. Contiene toda la estructura del sitio — categorias, productos, paginas, menus. Descargable por cualquiera. A WordPress export file importable into another installation. Contains the site's entire structure — categories, products, pages, menus. Downloadable by anyone.

Archivos de importacion del tema (todavia vivos desde 2021) Theme import files (still live since 2021)

  • content-...xml Exportacion WXR — todas las paginas, productos, categorias, etiquetas WXR export — all pages, products, categories, tags
  • options-...dat Volcado de opciones de WordPress — claves API, tokens, config de WooCommerce WordPress options dump — API keys, tokens, WooCommerce config
  • widgets-...json Configuracion completa de widgets — disposicion de la tienda, filtros Full widget configuration — shop layout, filters
  • slider-...zip Exportacion de Revolution Slider — archivo ZIP descargable Revolution Slider export — downloadable ZIP archive
Archivos creados una sola vez en junio de 2021 durante la instalacion del tema. Nadie los limpio. Llevan cinco anos ahi, publicos. Contienen el plano de como se armo el sitio. Files created just once in June 2021 during the theme setup. Nobody cleaned them up. Five years sitting in public. They contain the blueprint of how the site was built.

Fuentes con licencia descargables (riesgo legal) Licensed fonts downloadable (legal risk)

Directorio: Directory: /wp-content/uploads/useanyfont/

  • sofia-pro-regular.woff2 Sofia Pro — tipografia paga (27 KB, descarga directa) Sofia Pro — paid typeface (27 KB, direct download)
  • sofia-pro-light.woff2 Sofia Pro Light — misma familia, tambien paga Sofia Pro Light — same family, also paid
  • Goldenbook-Light.woff2 Goldenbook Light — tipografia paga Goldenbook Light — paid typeface
Tipografias con licencia comercial expuestas para descarga directa. Cualquiera las baja sin pagar. Si la fundicion tipografica lo nota, hay un problema legal — no hipotetico, las fundiciones escanean la web buscando exactamente esto. Commercially licensed typefaces exposed for direct download. Anyone grabs them without paying. If the type foundry notices, there's a legal issue — not hypothetical, foundries scan the web looking for exactly this.

8 anos de archivos subidos — navegables mes por mes 8 years of uploads — browseable month by month

Directorio raiz: Root directory: /wp-content/uploads/

2019/ · 2020/ · 2021/ · 2022/ · 2023/ · 2024/ · 2025/ · 2026/

+15 subdirectorios de plugins: +15 plugin subdirectories: aios/ · ao_ccss/ · cache/ · formcraft3/ · js_composer/ · maxmegamenu/ · merlin-wp/ · revslider/ · seedprod-help-docs/ · ultimatemember/ · useanyfont/ · wp-sheet-editor-universal-sheet/ · wpml/
Cada imagen de producto, cada foto, cada archivo subido desde 2019 — todo navegable. Un competidor descarga el catalogo completo de imagenes. Un atacante ve cuando se sube contenido nuevo (la ultima actividad fue en enero 2026). Los directorios de plugins revelan que herramientas internas estan en uso. Every product image, every photo, every file uploaded since 2019 — all browseable. A competitor downloads the full image catalog. An attacker sees when new content is uploaded (last activity was January 2026). The plugin directories reveal which internal tools are in use.

Reglas del firewall de seguridad (AIOS) expuestas Security firewall rules (AIOS) exposed

Directorio: Directory: /wp-content/uploads/aios/firewall-rules/

Directory listing: settings.php
El listado confirma que AIOS esta activo y revela donde guarda su configuracion de firewall. Aunque el archivo PHP se ejecuta en el servidor (y no muestra codigo), el hecho de que el directorio sea navegable le dice al atacante exactamente que buscar. The listing confirms AIOS is active and reveals where it stores its firewall config. While the PHP file is executed server-side (not showing source), the browseable directory tells an attacker exactly what to look for.

S3. Cero encabezados de seguridad HTTP S3. Zero HTTP security headers

CriticoCritical

El sitio no envia ningun encabezado de seguridad. Cada respuesta carece de todos los siguientes. Esto deja el sitio completamente expuesto a clickjacking (incrustacion en iframe), ataques de deteccion de tipo MIME, reflexion de cross-site scripting y filtracion de informacion. The site sends no security headers at all. Every response is missing all of the following. This leaves the site wide open to clickjacking (iframe embedding), MIME-type sniffing attacks, cross-site scripting reflection, and information leakage.

Header EstadoStatus Riesgo si faltaRisk if missing
Strict-Transport-SecurityAusenteMissingLos usuarios pueden ser degradados a HTTPUsers can be downgraded to HTTP
X-Frame-OptionsAusenteMissingClickjacking — el sitio puede incrustarse en un iframe de un atacanteClickjacking — site can be embedded in attacker's iframe
X-Content-Type-OptionsAusenteMissingAtaques de deteccion de tipo MIME en uploadsMIME-type sniffing attacks on uploads
Content-Security-PolicyAusenteMissingSin mitigacion XSS, sin restriccion de scripts inlineNo XSS mitigation, no restriction on inline scripts
Referrer-PolicyAusenteMissingURL de referencia completa filtrada a sitios externosFull referrer URL leaked to external sites
Permissions-PolicyAusenteMissingSin control de acceso a APIs del navegador (camara, microfono, geolocalizacion)No control over browser API access (camera, mic, geolocation)
Solve → Agregar a .htaccess: Add to .htaccess:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
O usar un plugin como "HTTP Headers" o "Security Headers" si no hay acceso a .htaccess. Or use a plugin like "HTTP Headers" or "Security Headers" if there's no access to .htaccess.
Los encabezados de seguridad son instrucciones que el sitio le da al navegador: "no me pongas en un iframe," "no adivines el tipo de archivo," "usa siempre HTTPS." Sin ellos, el navegador no tiene barandillas — y un atacante puede incrustar el sitio en una pagina falsa, ejecutar archivos peligrosos, o degradar la conexion. No hacen al sitio invencible, pero sin ellos las puertas estan abiertas. Security headers are instructions the site gives the browser: "don't put me in an iframe," "don't guess file types," "always use HTTPS." Without them, the browser has no guardrails — and an attacker can embed the site in a fake page, execute dangerous files, or downgrade the connection. They don't make the site invincible, but without them the doors are open.

S4. Ruta del sistema de archivos del servidor filtrada en log publico S4. Server filesystem path leaked in public log

AltoHigh

El asistente de configuracion Merlin WP dejo un log de depuracion en /wp-content/uploads/merlin-wp/main.log que es publicamente accesible. Revela la ruta completa del sistema de archivos del servidor: /home/ecstaticcrafts/public_html/. Esta es informacion valiosa para un atacante — confirma el nombre de la cuenta de hosting y la estructura de directorios, lo que facilita ataques de path traversal e inclusion de archivos locales. The Merlin WP setup wizard left a debug log at /wp-content/uploads/merlin-wp/main.log that's publicly accessible. It reveals the full server filesystem path: /home/ecstaticcrafts/public_html/. This is valuable intelligence for an attacker — it confirms the hosting account name and directory structure, which aids in path traversal and local file inclusion attacks.

File: /wp-content/uploads/merlin-wp/main.log
Leaked path: /home/ecstaticcrafts/public_html/wp-content/uploads/2021/06/content-2021-06-28__13-53-10.xml
Date in log: 2021-06-28 (theme initial setup — never cleaned up)
Cuando se configuro el tema en 2021, el asistente de instalacion dejo un log publico. Contiene la ruta interna del servidor — como dejar un post-it con la direccion de tu casa en el tablero del edificio. Por si solo no es peligroso; combinado con otras pistas, es una pieza mas del rompecabezas. Lleva ahi cinco anos. When the theme was set up in 2021, the installer left a public log behind. It contains the server's internal path — like leaving a post-it with your home address on the building's bulletin board. Not dangerous alone; combined with other clues, it's a puzzle piece. It's been sitting there for five years.
Solve → Eliminar el directorio /wp-content/uploads/merlin-wp/ completo. El asistente de configuracion del tema solo se usa una vez durante la instalacion inicial. Delete the entire /wp-content/uploads/merlin-wp/ directory. The theme setup wizard is only used once during initial installation.

S5. Directorio de reglas del firewall de AIOS expuesto S5. AIOS firewall rules directory exposed

AltoHigh

All-In-One Security (AIOS) esta instalado, pero su directorio de reglas de firewall en /wp-content/uploads/aios/firewall-rules/ es publicamente navegable. Esto expone settings.php y potencialmente revela que reglas de firewall estan activas — dando a un atacante un mapa de que protecciones existen y como evitarlas. All-In-One Security (AIOS) is installed, but its firewall rules directory at /wp-content/uploads/aios/firewall-rules/ is publicly browseable. This exposes settings.php and potentially reveals which firewall rules are active — giving an attacker a map of what protections exist and how to bypass them.

Directory: /wp-content/uploads/aios/firewall-rules/ → 200, browseable
Files: settings.php
El sitio tiene un plugin de seguridad (AIOS). Bien. Pero las reglas que usa para proteger el sitio estan en una carpeta que cualquiera puede navegar — como instalar un sistema de alarma y pegar el codigo en la puerta. The site has a security plugin (AIOS). Good. But the rules it uses to protect the site live in a folder anyone can browse — like installing an alarm system and taping the code to the front door.
Solve → Se corrige automaticamente con S2 (deshabilitar el listado de directorios). Adicionalmente, AIOS deberia guardar la configuracion fuera del directorio de uploads. Revisar la configuracion de AIOS para una opcion de "Mover archivos de configuracion". Solved automatically by S2 (disabling directory listing). Additionally, AIOS should store config outside of the uploads directory. Check AIOS settings for a "Move config files" option.

S6. Versiones de plugins completamente enumerables — habilita ataques CVE dirigidos S6. Plugin versions fully enumerable — enables targeted CVE attacks

AltoHigh

Multiples plugins exponen sus numeros de version exactos a traves de archivos readme.txt y parametros de cadena de consulta en el codigo fuente HTML. Un atacante puede cruzar estos datos con bases de datos publicas de CVE (WPScan, NVD) para encontrar exploits conocidos que funcionen con estas versiones exactas. Multiple plugins expose their exact version numbers through readme.txt files and query string parameters in the HTML source. An attacker can cross-reference these against public CVE databases (WPScan, NVD) to find known exploits that work on these exact versions.

WooCommerce: 10.5.2 (readme.txt)
WPML: 4.9.0 (readme.txt + HTML meta)
Revolution Slider: 6.7.41 (JS query string in HTML)
Max Mega Menu: 3.6.2 (readme.txt)
WordPress: 6.9.4 (HTML meta generator tag)
Yoast SEO: 27.0 (HTML comment)
Cada plugin publica su numero de version exacto — un letrero en la puerta que dice "cerradura modelo X, version 3.6.2." Los bots de ataque mantienen listas de que versiones son vulnerables. Publicar las versiones es ofrecerles el mapa. Every plugin publishes its exact version number — a sign on the door saying "lock model X, version 3.6.2." Attack bots maintain lists of which versions are vulnerable. Publishing the versions hands them the map.
Solve → (a) Mantener todos los plugins y el core de WP actualizados — la mejor defensa contra ataques basados en CVE. (b) Eliminar readme.txt y readme.html de la raiz de WP. (c) Eliminar las versiones en cadenas de consulta de los activos (agregar define('SCRIPT_DEBUG', false) y usar un plugin como "Remove WP Version" para eliminar las etiquetas generator). (d) Deshabilitar el listado de directorios (S2) para evitar la navegacion de readme.txt en los directorios de plugins. (a) Keep all plugins and WP core up to date — the best defense against CVE-based attacks. (b) Remove readme.txt and readme.html from the WP root. (c) Remove version query strings from enqueued assets (add define('SCRIPT_DEBUG', false) and use a plugin like "Remove WP Version" to strip generator tags). (d) Disable directory listing (S2) to prevent readme.txt browsing in plugin dirs.

S7. wp-cron.php accesible publicamente — vector de amplificacion DDoS S7. wp-cron.php publicly accessible — DDoS amplification vector

MedioMedium

wp-cron.php devuelve 200 y activa el sistema cron de WordPress. Un atacante puede enviar miles de solicitudes a este endpoint para sobrecargar el servidor con tareas programadas. En un servidor que ya tarda ~3.8s en renderizar una pagina, esto provocaria un agotamiento rapido de recursos. wp-cron.php returns 200 and triggers WordPress's cron system. An attacker can send thousands of requests to this endpoint to overload the server with scheduled tasks. On a server that already takes ~3.8s to render a page, this would cause rapid resource exhaustion.

WordPress tiene un programador interno (wp-cron) que ejecuta tareas como enviar correos y verificar actualizaciones. Cualquiera en internet puede activarlo visitando una URL. Mil peticiones simultaneas a esa URL es como mil personas pidiendo "ejecutar todas las actualizaciones" al mismo tiempo — en un servidor ya lento, eso tira el sitio. WordPress has an internal scheduler (wp-cron) that runs tasks like sending emails and checking for updates. Anyone on the internet can trigger it by visiting a URL. A thousand simultaneous requests to that URL is a thousand people asking "run all updates" at once — on an already-slow server, that takes the site down.
Solve → Deshabilitar WP-Cron via wp-config.php: define('DISABLE_WP_CRON', true); y configurar un cron real del sistema en el servidor (*/15 * * * * curl -s https://ecstaticcrafts.com/wp-cron.php). Esto tambien mejora el rendimiento ya que WordPress no verificara el cron en cada carga de pagina. Disable WP-Cron via wp-config.php: define('DISABLE_WP_CRON', true); and set up a real system cron on the server (*/15 * * * * curl -s https://ecstaticcrafts.com/wp-cron.php). This also improves performance since WordPress won't check cron on every page load.

S8. wp-admin/install.php accesible S8. wp-admin/install.php accessible

MedioMedium

La pagina de instalacion de WordPress es publicamente accesible y confirma "You appear to have already installed WordPress" junto con un enlace directo a la pagina de inicio de sesion. Si bien no es directamente explotable en su estado actual, le confirma a un atacante que esto es una instalacion de WordPress y proporciona un punto de apoyo para mayor reconocimiento. The WordPress installation page is publicly accessible and confirms "You appear to have already installed WordPress" along with a direct link to the login page. While not directly exploitable in its current state, it confirms to an attacker that this is a WordPress installation and provides a fingerhold for further reconnaissance.

La pagina de instalacion de WordPress sigue publica. Muestra "ya instalado" y da el enlace directo al login. No se puede reinstalar desde ahi, pero le confirma al curioso que es un sitio WordPress y le dice exactamente donde tocar. The WordPress installation page is still public. It shows "already installed" and links straight to the login page. You can't reinstall from there, but it confirms to a curious visitor that it's a WordPress site and tells them exactly where to knock.
Solve → Bloquear acceso a install.php via .htaccess: Block access to install.php via .htaccess:
<Files install.php>
Order allow,deny
Deny from all
</Files>

S9. Enumeracion de usuarios via archivos de autor S9. User enumeration via author archives

MedioMedium

Solicitar /?author=1 devuelve una redireccion 301 a la pagina de inicio (confirmando que el usuario ID 1 existe), mientras que /?author=2 devuelve 404. Esto permite a un atacante enumerar IDs de usuario validos y, combinado con vulnerabilidades de plugins, intentar ataques de relleno de credenciales o fuerza bruta en cuentas confirmadas. Requesting /?author=1 returns a 301 redirect to the homepage (confirming user ID 1 exists), while /?author=2 returns 404. This allows an attacker to enumerate valid user IDs and, when combined with plugin vulnerabilities, attempt credential stuffing or brute-force attacks on confirmed accounts.

?author=1 → HTTP 301 (user exists)
?author=2 → HTTP 404 (no user)
Agregando ?author=1 a la URL, cualquiera puede verificar si un usuario con ese ID existe. 301 = existe, 404 = no. Permite filtrar usuarios reales antes de intentar adivinar contrasenas — como revisar que apartamentos estan ocupados antes de forzar cerraduras. Adding ?author=1 to the URL lets anyone check if a user with that ID exists. 301 = exists, 404 = no. It lets an attacker filter out real accounts before guessing passwords — like checking which apartments are occupied before picking locks.
Solve → AIOS (ya instalado) tiene una opcion para deshabilitar los archivos de autor. Habilitarla: AIOS → Brute Force → Login Lockout. Alternativamente, agregar a .htaccess: AIOS (already installed) has a setting to disable author archives. Enable it: AIOS → Brute Force → Login Lockout. Alternatively, add to .htaccess:
RewriteRule ^/?author=([0-9]*) / [R=301,L]

Lo que esta bien asegurado What's properly secured

No todo esta mal — esto esta correctamente configurado: Not everything is bad — these are configured correctly:

VerificacionCheck ResultadoResult EstadoStatus
API WooCommerce (productos, pedidos, clientes)WooCommerce API (products, orders, customers)403 ForbiddenOK
API REST de WPMLWPML REST API403 ForbiddenOK
wp-login.php404 (hidden/moved)OK
Metodo TRACETRACE method405 Method Not AllowedOK
Redireccion HTTP → HTTPSHTTP → HTTPS redirect301 redirect, X-Redirect-By: WordPressOK
Redireccion www → sin wwwwww → non-www redirect301 redirectOK
.git/config404OK
.env301 → homepage (rewrite, not exposed)OK
wp-config.php403 ForbiddenOK
Copias de seguridad de wp-config.phpwp-config.php backupsAll 403OK
phpinfo.php / info.php404OK
Certificado TLSTLS certificateLet's Encrypt R13, valid until 2026-07-05, wildcard *.ecstaticcrafts.comOK
debug.log404OK
Endpoint de usuarios via REST APIREST API user endpointEmpty/blockedOK

Correcciones de seguridad, en orden Security fixes, in order

  1. Deshabilitar TLS 1.0 y 1.1 Disable TLS 1.0 and 1.1
    Establecer SSLProtocol -all +TLSv1.2 +TLSv1.3 en la configuracion de Apache. Obligatorio para el cumplimiento de PCI DSS en cualquier tienda WooCommerce que procese pagos. Si se usa Cloudflare (de la solucion de rendimiento #2), es un solo interruptor. Set SSLProtocol -all +TLSv1.2 +TLSv1.3 in Apache config. Mandatory for PCI DSS compliance on any WooCommerce store handling payments. If using Cloudflare (from performance fix #2), this is a single toggle.
    ImpactoImpact: Cumplimiento PCI + seguridad de protocoloPCI compliance + protocol security EsfuerzoEffort: 5 min
  2. Deshabilitar el listado de directorios Disable directory listing
    Agregar Options -Indexes al .htaccess raiz. Corrige S2, S5 (exposicion de AIOS) y bloquea la navegacion de archivos de plugins. Una correccion de una linea que cierra la fuga de informacion mas peligrosa. Add Options -Indexes to the root .htaccess. Fixes S2, S5 (AIOS exposure), and blocks plugin file browsing. One-line fix that closes the most dangerous information leak.
    ImpactoImpact: Cierra la enumeracion completa de archivos del sitioCloses full site file enumeration EsfuerzoEffort: 2 min
  3. Agregar encabezados de seguridad HTTP Add HTTP security headers
    Agregar HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy y Permissions-Policy al .htaccess. El CSP puede agregarse despues (requiere pruebas cuidadosas con 57 scripts + dominios externos). Add HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy to .htaccess. CSP can be added later (needs careful testing with 57 scripts + external domains).
    ImpactoImpact: Prevencion de clickjacking + MIME + XSSClickjacking + MIME + XSS prevention EsfuerzoEffort: 10 min
  4. Eliminar el log y directorio merlin-wp Delete the merlin-wp log and directory
    Eliminar /wp-content/uploads/merlin-wp/ completamente. Contiene la fuga de ruta del servidor y nunca se necesita despues de la configuracion inicial del tema. Remove /wp-content/uploads/merlin-wp/ entirely. It contains the server path leak and is never needed after initial theme setup.
    ImpactoImpact: Elimina la divulgacion de ruta del servidorRemoves server path disclosure EsfuerzoEffort: 1 min
  5. Eliminar informacion de versiones Strip version information
    Eliminar readme.html y license.txt de la raiz de WP. Quitar la etiqueta meta generator. Eliminar versiones en cadenas de consulta de JS/CSS. Esto dificulta los ataques CVE dirigidos (seguridad por oscuridad, pero igual tiene valor). Remove readme.html and license.txt from the WP root. Remove the generator meta tag. Strip query string versions from JS/CSS. This makes targeted CVE attacks harder (security through obscurity, but still valuable).
    ImpactoImpact: Reduce la superficie de ataques dirigidosReduces targeted attack surface EsfuerzoEffort: 15 min
  6. Deshabilitar wp-cron.php y configurar cron del servidor Disable wp-cron.php and set up server cron
    Agregar define('DISABLE_WP_CRON', true); a wp-config.php y configurar una tarea cron del lado del servidor. Previene la amplificacion DDoS y mejora ligeramente el rendimiento de carga de paginas. Add define('DISABLE_WP_CRON', true); to wp-config.php and configure a server-side cron job. Prevents DDoS amplification and slightly improves page load performance.
    ImpactoImpact: Vector DDoS cerrado + mejora menor de rendimientoDDoS vector closed + minor perf gain EsfuerzoEffort: 10 min

Versiones de software identificadas Identified software versions

Todas las versiones detectadas externamente desde respuestas publicas. Mantenerlas actualizadas es la practica de seguridad mas importante. All versions detected externally from public responses. Keeping these up to date is the single most important security practice.

SoftwareSoftware VersionVersion FuenteSource
WordPress6.9.4Meta generator HTMLHTML meta generator
WooCommerce10.5.3 (meta) / 10.5.2 (readme)Meta HTML + readme.txtHTML meta + readme.txt
WPML4.9.0Meta HTML + readme.txtHTML meta + readme.txt
Revolution Slider6.7.41Cadena de consulta en activo JSJS asset query string
Yoast SEO27.0Comentario HTMLHTML comment
Max Mega Menu3.6.2readme.txt
Goya themedetected (child theme)Codigo fuente HTMLHTML source
Apacheversion hiddenEncabezado ServerServer header
Certificado TLSTLS certificateLet's Encrypt R13, expires 2026-07-05OpenSSL

Progreso: lo que se resolvio el 2026-04-10 Progress: what was solved on 2026-04-10

Sesion de optimizacion en el panel de WordPress. Estado antes/despues de cada hallazgo: Optimization session in the WordPress admin panel. Before/after state of each finding:

TTFB
1.2s
Antes: 3.82s · Mejora: 69%Before: 3.82s · Improvement: 69%
Carga TotalTotal Load
1.5s
Antes: 4.5s · Mejora: 67%Before: 4.5s · Improvement: 67%
Scripts
59
Emoji, oEmbed, generator eliminadosEmoji, oEmbed, generator removed
HTTP
1.1
Sin cambio — necesita CloudflareNo change — needs Cloudflare

Rendimiento — lo que se hizoPerformance — what was done

AccionAction DetalleDetail EstadoStatus
WP Rocket — cache de paginaWP Rocket — page cacheActivado + optimizado. Cache confirmado via firma HTML.Activated + optimized. Cache confirmed via HTML signature.HechoDone
WP Rocket — JS diferidoWP Rocket — deferred JSLoad JS Deferred + Delay JS Execution activadosLoad JS Deferred + Delay JS Execution enabledHechoDone
WP Rocket — CSS optimizadoWP Rocket — CSS optimizationMinify CSS + Remove Unused CSS activadosMinify CSS + Remove Unused CSS enabledHechoDone
WP Rocket — LazyLoadWP Rocket — LazyLoadImagenes, iframes, CSS backgroundsImages, iframes, CSS backgroundsHechoDone
WP Rocket — HeartbeatWP Rocket — HeartbeatBackend + frontend deshabilitados, editor reducidoBackend + frontend disabled, editor reducedHechoDone
WP Rocket — Base de datosWP Rocket — Database1,972 transients limpiados. Limpieza diaria automatica.1,972 transients cleaned. Daily auto-cleanup enabled.HechoDone
WP Rocket — PrecargaWP Rocket — PreloadPrecarga de paginas + precarga de enlaces activadasPage preloading + link preloading enabledHechoDone
Limpieza de pluginsPlugin cleanup~24 inactivos eliminados + 12 activos desactivados. De ~55 a ~25 plugins activos.~24 inactive deleted + 12 active deactivated. From ~55 to ~25 active plugins.HechoDone
Cache de /tienda/ reparado/tienda/ cache fixedEliminada de la lista "Never Cache" — la pagina de tienda ahora se sirve desde cacheRemoved from "Never Cache" list — shop page now served from cacheHechoDone
Asset CleanUp — instalado— installedPlugin instalado y configurado. Detecta WP Rocket y evita duplicar minificacion.Plugin installed and configured. Detects WP Rocket and avoids duplicating minification.HechoDone
Asset CleanUp — Limpieza HTML— HTML cleanupEliminados: emoji scripts, etiqueta meta generator (esconde version WP), oEmbed, RSD, Windows Live Writer, wlwmanifest, REST API linksRemoved: emoji scripts, generator meta tag (hides WP version), oEmbed, RSD, Windows Live Writer, wlwmanifest, REST API linksHechoDone
Asset CleanUp — XML-RPC— XML-RPCXML-RPC deshabilitado — vector comun de fuerza bruta cerradoXML-RPC disabled — common brute force attack vector closedHechoDone
Cloudflare CDNRequiere cambio de nameservers — bloqueado por ahoraRequires nameserver change — blocked for nowPendientePending
Asset CleanUp — Descarga selectiva por pagina— Per-page script unloadingNo realizado aun — descargar scripts de WooCommerce/FormCraft/Wishlist en homepage y paginas no-tiendaNot done yet — unload WooCommerce/FormCraft/Wishlist scripts on homepage and non-shop pagesEn progresoIn progress

Seguridad — lo que se resolvioSecurity — what was solved

HallazgoFinding AntesBefore DespuesAfter EstadoStatus
S1. TLS 1.0HabilitadoEnabledBloqueadoBlockedResueltoSolved
S1. TLS 1.1HabilitadoEnabledBloqueado (verificado Fase 3)Blocked (verified Phase 3)ResueltoSolved
S2. Listado de directoriosS2. Directory listingHTTP 200 — navegablebrowseableHTTP 404 — bloqueadoblockedResueltoSolved
S3. Encabezados de seguridadS3. Security headersNingunoNoneFuncionan en subdominios, no en dominio principal (ver S18)Work on subdomains, not on main domain (see S18)ParcialPartial
S4. Log de Merlin (ruta del servidor)S4. Merlin log (server path)HTTP 200HTTP 200 — directorio bloqueado pero archivo aun accesibledirectory blocked but file still accessibleParcialPartial
S5. Dir de reglas AIOSS5. AIOS rules dirHTTP 200 — navegablebrowseableHTTP 404ResueltoSolved
S6. Versiones de plugins expuestasS6. Plugin versions exposedreadme.html, license.txt, generator meta404 + meta generator eliminadaremovedResueltoSolved
S7. wp-cron.phpHTTP 200Aun accesible — necesita acceso a wp-config.phpStill accessible — needs wp-config.php accessPendientePending
S8. install.phpHTTP 200HTTP 404ResueltoSolved
S9. Enumeracion de autoresS9. Author enumerationHTTP 301 — usuario confirmadouser confirmedHTTP 301 — redirige pero aun detecta el usuarioredirects but still detects userParcialPartial
Archivos por defecto de WPWP default filesreadme.html, license.txt accesiblesaccessibleEliminados + auto-eliminar tras actualizacionesDeleted + auto-delete on updatesResueltoSolved
Edicion de archivos PHPPHP file editingHabilitadoEnabledDeshabilitado via AIOSDisabled via AIOSResueltoSolved
HotlinkingPermitidoAllowedBloqueado via AIOSBlocked via AIOSResueltoSolved

Progreso: lo que se resolvio el 2026-04-17 Progress: what was solved on 2026-04-17

Sesion de acceso directo al servidor. Se establecio SSH + WP-CLI + MCP para control completo del stack, seguido de una pasada de recon profundo y nueve mejoras rapidas (Q1-Q9) sin impacto al cliente. Direct server-access session. Established SSH + WP-CLI + MCP for full stack control, followed by a deep recon pass and nine quick wins (Q1-Q9) with no client-facing impact.

AutoloadAutoload
587KB
Antes: 715 KB · Reduccion: 18%Before: 715 KB · Reduction: 18%
Log de erroresError log
0MB
Antes: 15 MB · Truncado (tail guardado)Before: 15 MB · Truncated (tail saved)
Limite de subidaUpload limit
32MB
Antes: 2 MB · 16x masBefore: 2 MB · 16x more
Acceso SSHSSH access
OK
WP-CLI + MCP configuradosWP-CLI + MCP configured

Acceso — nuevas capacidades operativasAccess — new operational capabilities

CapaLayer DetalleDetail EstadoStatus
SSHLlave ed25519 autorizada en cPanel. Acceso directo como usuario ecstaticcrafts@cp.ecstaticcrafts.com via puerto 22.ed25519 key authorized in cPanel. Direct access as ecstaticcrafts@cp.ecstaticcrafts.com via port 22.ActivoActive
WP-CLIv2.12 instalado en ~/bin/wp. Control completo de WP, WC, DB, opciones, usuarios, cron, plugins sin pasar por la UI.v2.12 installed at ~/bin/wp. Full control of WP, WC, DB, options, users, cron, plugins without going through the UI.ActivoActive
MCP (WordPress)mcp-wordpress v3.1.21 configurado. Usuario ormus-ops (admin) + Application Password. 59 herramientas REST expuestas nativamente al asistente.mcp-wordpress v3.1.21 configured. ormus-ops user (admin) + Application Password. 59 REST tools exposed natively to the assistant.Activo (requiere reinicio)Active (restart required)

Seguridad — hallazgos nuevos y remediadosSecurity — new and remediated findings

HallazgoFinding AntesBefore DespuesAfter EstadoStatus
Permisos de wp-config.phpwp-config.php permissions644 — DB pass legible por otros usuarios del SODB pass readable by other OS users600 — solo el propietarioowner onlyResueltoSolved
WP_ALLOW_REPAIR anonimoAnonymous WP_ALLOW_REPAIR/wp-admin/maint/repair.php habilitado y ejecutable sin autenticacionenabled and executable without authenticationConstante removida — pagina solo muestra instruccionConstant removed — page only shows instructionResueltoSolved
Ejecucion PHP en /uploads/PHP execution in /uploads/Permitido — ej. aios/firewall-rules/settings.php devuelve 200Allowed — e.g. aios/firewall-rules/settings.php returns 200Bloqueado via .htaccess FilesMatch (php|phtml|phar) — verificado 404Blocked via .htaccess FilesMatch (php|phtml|phar) — verified 404ResueltoSolved
Email de admin de WordPressWP admin emailweb@zuliatec.com (dev original, tercero)(original dev, third party)Sin cambio — requiere decision del clienteUnchanged — client decision requiredPendientePending
Cuentas admin de proveedoresVendor admin accountsweb2021, supportwpindeed, wpmlsupport — 3 admins externos activos— 3 external admins activeDocumentados, sin cambio — requiere decision del clienteDocumented, unchanged — client decision requiredPendientePending
AIOS bloqueo REST anonimoAIOS anonymous REST blockActivo — bloqueaba enumeracion /wp-json/usersActive — blocked /wp-json/users enumerationDesactivado (necesario para MCP). Mitigacion: IP allowlist + rate limiting pendienteDisabled (required for MCP). Mitigation: IP allowlist + rate limiting pendingCompromiso conscienteConscious tradeoff
AIOS bloqueo Application PasswordsAIOS Application Passwords blockActivoActiveDesactivado para permitir auth MCPDisabled to allow MCP authCompromiso conscienteConscious tradeoff

Rendimiento — lo que se hizoPerformance — what was done

AccionAction DetalleDetail ImpactoImpact EstadoStatus
Mu-plugin muerto eliminadoDead mu-plugin removedp3-profiler.php (stub de 94 bytes lanzando fatales desde 2024)(94-byte stub throwing fatals since 2024)Menos errores PHP por requestFewer PHP errors per requestHechoDone
Opciones autoload basuraStale autoload optionsp3_scan_2021 (58 KB), GTranslate (39 KB), evo_data_log (31 KB) — todas cargadas en cada requestp3_scan_2021 (58 KB), GTranslate (39 KB), evo_data_log (31 KB) — all loaded on every request-128 KB por pagina (715 KB → 587 KB)-128 KB per page (715 KB → 587 KB)HechoDone
Log de errores truncadoError log truncated15 MB → 0. Ultimas 2000 lineas guardadas en ~/_ormus_ops/logs/.15 MB → 0. Last 2000 lines saved to ~/_ormus_ops/logs/.15 MB de disco liberados15 MB disk freedHechoDone
ZIPs obsoletos en /plugins/Stale .zip files in /plugins/3 archivos (codecanyon-ump, 2x easypost) movidos a archivo3 files (codecanyon-ump, 2x easypost) moved to archive17 MB movidos de webroot17 MB moved out of webrootHechoDone
Backup de 1.5 GB en $HOME1.5 GB backup in $HOMEbackup_EC_18022026.zip movido fuera de $HOME raiz a ~/_ormus_ops/archives/backup_EC_18022026.zip moved out of $HOME root to ~/_ormus_ops/archives/Archivado, pendiente eliminar tras backup frescoArchived, pending delete after fresh backupHechoDone
Limites PHP (admin uploads)PHP limits (admin uploads).user.ini: upload 2M→32M, post 8M→64M, memoria 512M, ejecucion 300s.user.ini: upload 2M→32M, post 8M→64M, memory 512M, execution 300sAdmin puede subir imagenes modernasAdmin can upload modern-size imagesHecho (FPM TTL 5 min)Done (FPM TTL 5 min)
Transients expiradosExpired transients6 filas eliminadas. Cron diario ya programado (delete_expired_transients).6 rows deleted. Daily cron already scheduled (delete_expired_transients).Mantenimiento continuoOngoing maintenanceHechoDone
Compresion gzip restauradaGzip compression restoredWP Rocket descomprimia antes de servir. Solucion: zlib.output_compression=On en .user.ini. Homepage: 232 KB → 44.5 KB (-81%). EN: 387 KB → 54 KB (-86%). Cold: 142 KB → 30 KB (-79%).WP Rocket was decompressing before serving. Fix: zlib.output_compression=On in .user.ini. Homepage: 232 KB → 44.5 KB (-81%). EN: 387 KB → 54 KB (-86%). Cold: 142 KB → 30 KB (-79%).Peso de pagina 5x menor5x smaller page weightHechoDone

Hallazgos operativos nuevos (informacion)New operational findings (info)

INFO

HPOS no habilitado — 3,463 ordenes en tabla legacyHPOS not enabled — 3,463 orders on legacy table

WooCommerce sigue usando la tabla wp_posts para ordenes (cklvz_posts). El nuevo almacenamiento de alto rendimiento (HPOS / cklvz_wc_orders) no esta activo. Migrar reduce tiempo de carga del panel de ordenes significativamente, pero requiere verificar compatibilidad con el plugin "WooCommerce Legacy REST API" instalado.WooCommerce still uses wp_posts for orders (cklvz_posts). The new High-Performance Order Storage (HPOS / cklvz_wc_orders) is not active. Migrating significantly reduces admin order page load times, but requires verifying compatibility with the installed "WooCommerce Legacy REST API" plugin.

INFO

1,047 ordenes en wc-on-hold (historico)1,047 orders in wc-on-hold (all-time)

Estado on-hold tipicamente significa ordenes esperando confirmacion de transferencia bancaria. La mayoria probablemente son ordenes abandonadas. Limpiar con wc order cancel --status=on-hold --before="180 days ago" reduciria cklvz_posts/cklvz_postmeta significativamente. Requiere decision del cliente sobre ciclo de vida de datos de clientes.On-hold status typically means orders awaiting bank transfer confirmation. Most are likely abandoned. Cleaning via wc order cancel --status=on-hold --before="180 days ago" would shrink cklvz_posts/cklvz_postmeta significantly. Requires client decision on customer data lifecycle.

HIGH

WPML genera el 38% de errores PHP recientesWPML generates 38% of recent PHP errors

De las ultimas 2000 lineas del log de errores, 771 corresponden a sitepress-multilingual-cms. Patron: cuando la conexion DB hace hipo, SitePress::get_current_language() recibe null y el filtro locale explota con Call to a member function get_requested_lang() on null. Cosmetico en condiciones normales pero oscurece bugs reales. Es el mayor contribuyente a ruido en logs. Ademas WPML + plugins hermanos ocupan ~152 MB de disco en plugins/.Of the last 2000 lines of the error log, 771 come from sitepress-multilingual-cms. Pattern: when DB connection hiccups, SitePress::get_current_language() receives null and the locale filter explodes with Call to a member function get_requested_lang() on null. Cosmetic under normal conditions but obscures real bugs. The single biggest contributor to log noise. Additionally, WPML + sibling plugins consume ~152 MB of disk in plugins/.

INFO

app.ecstaticcrafts.com no esta en este cPanelapp.ecstaticcrafts.com is not on this cPanel

Las carpetas ~/public_html/app.ecstaticcrafts.com/ y ~/public_html/api.ecstaticcrafts.com/ son directorios placeholder de 8 KB (verificacion Google + index.html). La aplicacion React de registro de arte y el backend Django viven en un servidor separado. Los hallazgos S11 y S21-S26 (source maps expuestos, DEBUG=True en Django, etc.) no pueden remediarse desde este cPanel — requieren acceso al servidor que hospeda realmente el subdominio app/api.The folders ~/public_html/app.ecstaticcrafts.com/ and ~/public_html/api.ecstaticcrafts.com/ are 8 KB placeholder directories (Google verification + index.html). The React art ownership registry app and Django backend live on a separate server. Findings S11 and S21-S26 (exposed source maps, DEBUG=True in Django, etc.) cannot be remediated from this cPanel — they require access to the actual hosting server for the app/api subdomains.

Siguientes pasos (pendientes — requieren decision del cliente o entorno de staging)Next steps (pending — require client decision or staging environment)

AccionAction Por que esperaWhy it waits
PHP 7.4 → 8.2PHP 7.4 → 8.2Mejora 30-50% en velocidad de generacion. Requiere clonar a staging y probar revslider, js_composer, plugins de CodeCanyon antes de cambiar produccion.30-50% speed improvement in page generation. Requires staging clone and testing of revslider, js_composer, CodeCanyon plugins before flipping production.
CloudflareTTFB global <200ms + HTTP/2/3 + Brotli. Requiere acceso a nameservers DNS + sign-off del cliente.Global TTFB <200ms + HTTP/2/3 + Brotli. Requires DNS nameserver access + client sign-off.
Migracion WC HPOSWC HPOS migrationVerificar primero que WC Legacy REST API no esta en uso por integraciones externas.First verify WC Legacy REST API is not in use by external integrations.
Limpieza de admins de proveedoresVendor admin cleanupEliminar web2021 / supportwpindeed / wpmlsupport tras confirmar con Diego que no estan troubleshooting activo.Remove web2021 / supportwpindeed / wpmlsupport after confirming with Diego none are actively troubleshooting.
9 plugins inactivos a eliminar9 inactive plugins to remove~50 MB ahorrados. Confirmar con Diego que ninguno es "apagado pero necesario".~50 MB saved. Confirm with Diego that none is "off but needed".
Limpieza wc-on-hold historicosHistoric wc-on-hold cleanupDecision del cliente sobre retencion de datos. ~800 ordenes >180 dias candidatas.Client decision on data retention. ~800 orders >180 days candidates.
Cambio admin_emailCambiar de web@zuliatec.com a email controlado por el cliente. Diego debe elegir destino.Change from web@zuliatec.com to a client-controlled email. Diego must pick destination.
Optimizacion WebP masivaBulk WebP optimization1.3 GB en uploads/ incluyendo 9 fotos de telefono de 5-7 MB en formcraft3. Requiere backup antes y prueba en subset.1.3 GB in uploads/ including 9 phone photos of 5-7 MB in formcraft3. Requires backup first and test on subset.

Progreso: lo que se resolvio el 2026-04-18 Progress: what was solved on 2026-04-18

Pasada SSH-only enfocada exclusivamente en velocidad. Limpieza profunda de tablas huerfanas de plugins inactivos y optimizacion de autoload + DB. Se descubrio que OPcache no esta disponible en este hosting (el proveedor lo excluyo de los paquetes EA-PHP). SSH-only pass focused exclusively on speed. Deep cleanup of orphan tables from inactive plugins and autoload + DB optimization. Discovered OPcache is not available on this hosting (the provider excluded it from EA-PHP packages).

Tamano de BDDB size
274MB
Antes: 750 MB · Reduccion: 63%Before: 750 MB · Reduction: 63%
AutoloadAutoload
384KB
Antes: 586 KB · Reduccion: 35%Before: 586 KB · Reduction: 35%
TTFB frioCold TTFB
3.7s
Piso bloqueado sin OPcacheFloor blocked without OPcache
TTFB EN frioEN cold TTFB
4.7s
Antes: 8.0s · Mejora: 41%Before: 8.0s · Improvement: 41%

Limpieza profunda de base de datosDeep database cleanup

TablaTable Plugin origenSource plugin AntesBefore DespuesAfter EstadoStatus
cklvz_wpml_mailswp-mail-logging (inactivo)(inactive)362 MB / 14,114 filasrows0 / 0TruncadoTruncated
cklvz_wsal_metadatawp-activity-log-for-woocommerce (inactivo)(inactive)65 MB0TruncadoTruncated
cklvz_wsal_occurrenceswp-activity-log-for-woocommerce (inactivo)(inactive)28 MB0TruncadoTruncated
cklvz_actionscheduler_actionswoocommerce1,592 completadascomplete0Limpiado (retencion)Cleaned (retention)

Autoload slim-down (9 opciones grandes + huerfano)Autoload slim-down (9 big options + orphan)

OpcionOption TamanoSize AccionAction
_transient_wp_core_block_css_files22 KBEliminado (transient que nunca debio autoload)Deleted (transient that should never autoload)
revslider-addons35 KBautoload=no (leido solo en pags del plugin)autoload=no (read only on plugin pages)
ihc_levels, ihc_user_fields47 KBautoload=no (indeed-membership-pro)(indeed-membership-pro)
evcal_options_evcal_1/2/3, evcal_styles70 KBautoload=no (eventON, cargado solo en pags de calendario)(eventON, loaded only on calendar pages)
woocommerce_wf_easypost_id_*33 KBautoload=no (easypost shipping)(easypost shipping)

Otros cambiosOther changes

AccionAction DetalleDetail EstadoStatus
DISABLE_WP_CRONHabilitado en wp-config.php. Quita el "impuesto cron" de requests de usuarios (jitter 100-500ms).Enabled in wp-config.php. Removes cron tax from user requests (100-500ms jitter).HechoDone
Cron real via cPanelReal cron via cPanelPendiente: Ormus debe agregar "cada 5 min: wp cron event run --due-now"Pending: Ormus must add "every 5 min: wp cron event run --due-now"Pendiente (accion manual)Pending (manual action)
WPML null-guard mu-pluginSuprime los 771 fatales de WPML durante estados dead_db. Cosmetico — solo deja de ensuciar logs.Suppresses the 771 WPML fatals during dead_db states. Cosmetic — just stops log pollution.HechoDone
BLOCKED

OPcache no disponible en el hostingOPcache not available on hosting

Probado: el modulo opcache.so no esta instalado para NINGUNA version de PHP disponible en este hosting (ea-php73, 74, 80, 81, 82, 83). get_loaded_extensions() no incluye "Zend OPcache". opcache_get_status() es undefined. APCu tampoco esta disponible. El proveedor de hosting excluyo estos modulos de sus paquetes EA-PHP — esto es inusual (la mayoria de hostings cPanel traen OPcache por defecto).Probed: the opcache.so module is not installed for ANY PHP version available on this hosting (ea-php73, 74, 80, 81, 82, 83). get_loaded_extensions() does not include "Zend OPcache". opcache_get_status() is undefined. APCu is also unavailable. The hosting provider excluded these modules from their EA-PHP packages — this is unusual (most cPanel hosts ship OPcache by default).

ImpactoImpact: cada request PHP re-parsea miles de archivos desde disco. Es la unica palanca dominante restante para TTFB frio. Sin OPcache, el piso se queda en ~3.7s.every PHP request re-parses thousands of files from disk. This is the only remaining dominant lever for cold TTFB. Without OPcache, the floor stays at ~3.7s.

AccionAction: abrir ticket al hosting solicitando instalar `ea-php82-php-opcache` (paquete oficial de cPanel). Con OPcache activado se espera reducir el TTFB frio a ~1.5-2s (~−50%). Borrador de ticket listo.open hosting ticket requesting installation of `ea-php82-php-opcache` (official cPanel package). With OPcache active we expect cold TTFB to drop to ~1.5-2s (~−50%). Ticket draft ready.

INFO

Version PHP real es 8.2.30 (ya) — no era 7.4 como creimosReal PHP version is 8.2.30 (already) — not 7.4 as we thought

La probe inicial via SSH mostraba PHP 7.4.33, pero era la version CLI por defecto del shell. El SAPI web (cgi-fcgi) realmente corre ea-php82 (PHP 8.2.30). Esto significa que el "upgrade a PHP 8" ya estaba hecho — el sitio se beneficia de los ~30% de mejora vs 7.4 que trae PHP 8.x a nivel de motor. Este descubrimiento re-enmarca el techo: los 3.7s frios son con PHP 8.2 sin OPcache, no con 7.4.The initial SSH probe showed PHP 7.4.33, but that was the shell's default CLI version. The web SAPI (cgi-fcgi) actually runs ea-php82 (PHP 8.2.30). This means the "PHP 8 upgrade" was already done — the site already benefits from the ~30% engine-level improvement over 7.4. This reframes the ceiling: the 3.7s cold is with PHP 8.2 without OPcache, not with 7.4.

Lo que falta: el cuello de botella del servidor What's left: the server bottleneck

DiagnosticoDiagnosis

Todas las optimizaciones de software posibles estan aplicadas. El TTFB bajo de 3.82s a ~1.2s. El piso de 1.2s es el servidor mismo — incluso sirviendo HTML estatico desde cache, tarda mas de un segundo en entregarlo. No hay configuracion de WordPress que arregle eso. La siguiente mejora requiere Cloudflare o mejor hosting. All possible software optimizations are applied. TTFB dropped from 3.82s to ~1.2s. The 1.2s floor is the server itself — even serving static HTML from cache, it takes over a second to deliver it. No WordPress configuration fixes that. The next improvement requires Cloudflare or better hosting.

Optimizamos toda la cocina: chefs mas rapidos, ingredientes pre-cortados, menu simplificado. El problema es que el mesero camina despacio. La pagina ya esta lista en milisegundos — el servidor tarda 1.2 segundos en entregarla al visitante. We optimized the whole kitchen: faster chefs, pre-cut ingredients, simpler menu. The problem is the waiter walks slowly. The page is ready in milliseconds — the server takes 1.2 seconds to hand it to the visitor.
Evidencia: confirmacion de que el servidor es hosting compartido Evidence: confirmation that the server is shared hosting

Seis pruebas independientes. Todas llegan a la misma conclusion: el sitio vive en un servidor cPanel compartido de una agencia pequena (Zuliatec), en un data center rentado (Telx, Nueva York). Six independent tests. All arrive at the same conclusion: the site lives on a shared cPanel server from a small agency (Zuliatec), in a rented data center (Telx, New York).

Prueba 1: cPanel detectado en el servidorTest 1: cPanel detected on server

GET /cpanel → HTTP 200 — "cPanel Redirect" page
GET /webmail → HTTP 200 — "cPanel Redirect" page
Las rutas /cpanel y /webmail responden con paginas oficiales de cPanel. Solo existen en servidores con cPanel instalado — el panel de control tipico de hosting compartido. Un VPS propio o un servidor dedicado no las tiene auto-creadas. The /cpanel and /webmail paths return official cPanel pages. They only exist on servers with cPanel installed — the typical shared hosting control panel. A self-managed VPS or dedicated server doesn't auto-create them.

Prueba 2: El proveedor es Zuliatec (agencia web venezolana)Test 2: The provider is Zuliatec (Venezuelan web agency)

Fuente: codigo HTML de ecstaticcrafts.com contiene referencias a Source: HTML source of ecstaticcrafts.com contains references to zuliatec.com

Meta description de zuliatec.com:
"Diseno Web, Desarrollo Web, Desarrollo de App, Desarrollo de Aplicaciones,
Marketing Digital, Servicios en la Nube y Consultoria Organizacional"
Zuliatec es una agencia pequena que hace diseno, desarrollo, y vende "servicios en la nube" (hosting). Cuando la misma agencia construye el sitio y lo hospeda, las quejas sobre velocidad llegan al escritorio del que decidio cuanto hardware asignar. No hay arbitro externo. Zuliatec is a small agency doing design, development, and selling "cloud services" (hosting). When the same agency builds the site and hosts it, speed complaints arrive at the desk of the person who decided how much hardware to allocate. No external referee.

Prueba 3: El sitio del proveedor tiene el mismo problemaTest 3: The provider's own site has the same problem

zuliatec.com TTFB: 3.01 segundos
ecstaticcrafts.com TTFB (antes de optimizar): 3.82 segundos
ecstaticcrafts.com TTFB (despues de optimizar): 1.2 segundos
El sitio del proveedor mismo tarda 3 segundos. Ecstatic Crafts es mas rapido ahora solo porque aplicamos WP Rocket y eliminamos ~30 plugins. El proveedor no optimizo nada — el cache no fue idea suya, la limpieza tampoco. The provider's own site takes 3 seconds. Ecstatic Crafts is faster now only because we applied WP Rocket and removed ~30 plugins. The provider optimized nothing — the cache wasn't their idea, the cleanup wasn't either.

Prueba 4: Data center de Telx (colocacion rentada)Test 4: Telx data center (rented colocation)

IP: 67.23.63.136
ASN: 36086
Propietario: TELX-LEGACY, US
Rango: 67.23.62.0/23
Ubicacion: Nueva York, EE.UU.
Telx es un data center mayorista — venden rack, energia y conexion. No venden hosting al publico. Zuliatec renta espacio ahi y revende hosting compartido encima. El arreglo es comun, pero coloca dos intermediarios entre Ecstatic Crafts y el hardware. Telx is a wholesale data center — they sell rack space, power, and connectivity. They don't sell retail hosting. Zuliatec rents space there and resells shared hosting on top of it. Common arrangement, but places two middlemen between Ecstatic Crafts and the hardware.

Prueba 5: Vecindario IP — 8 servidores identificados en el mismo rackTest 5: IP neighborhood — 8 servers identified in the same rack

Fuente: escaneo HTTP del bloque /24 67.23.63.0/24 + consulta a Shodan InternetDB para cada IP activa. Los resultados revelan un vecindario heterogeneo de servidores individuales colocados en el mismo rack Telx — algunos con problemas graves de parches. Source: HTTP scan of the /24 block 67.23.63.0/24 + Shodan InternetDB query for each active IP. The results reveal a heterogeneous neighborhood of individual servers colocated in the same Telx rack — some with severe patching problems.

IP Host Stack CVEs
.58(no hostname)Microsoft IIS 10 + ASP.NET + jQuery 1.7.26
.60nerdssupport.comMicrosoft IIS 10 + Windows0
.132(no hostname)Apache 2.4.58 / Ubuntu37
.133esriven.com + cp.esriven.comcPanel · Apache · Exim 4.96 · OpenSSH 7.427
.135tagscredit.com + kibovision.dinoiadevs.com.arNginx 1.14.2 + Directus CMS9
.136ecstaticcrafts.com + cp.ecstaticcrafts.comcPanel · Apache · Exim 4.95 · OpenSSH 7.4 · MySQL · Pure-FTPd32
.167(no hostname)OpenSSH 7.4 + VNC (5900) + SNMP — likely mgmt interface21
.171(no hostname)Apache 2.2.22 / Debian · PHP 5.4.45 · OpenSSH 6.0p1 (todos EOLall EOL)180+
Ocho servidores de propietarios distintos conviven en el mismo rack, mezclando Windows, Ubuntu, Debian, y distintas configuraciones. Uno de ellos (.171) corre Debian antiguo con mas de 180 CVEs — incluyendo PHP 5.4.45 y Apache 2.2.22, software que lleva casi una decada sin parches. Otro (.132) tiene 37 CVEs. El servidor de Ecstatic Crafts (.136) tiene 32 CVEs en su stack subyacente. WordPress puede estar al dia — el sistema operativo debajo no lo esta. Eight servers from different owners live in the same rack, mixing Windows, Ubuntu, Debian, and varying configurations. One of them (.171) runs ancient Debian with 180+ CVEs — including PHP 5.4.45 and Apache 2.2.22, software that hasn't seen patches in nearly a decade. Another (.132) has 37 CVEs. The Ecstatic Crafts server (.136) has 32 CVEs in its underlying stack. WordPress may be current — the operating system underneath is not.

Prueba 5b: Servidor "hermano" — esriven.comTest 5b: "Sibling" server — esriven.com

67.23.63.133 → esriven.com + cp.esriven.com
Stack identico a ecstaticcrafts.com: cPanel, Apache, Exim, OpenSSH 7.4
Puertos abiertos: 22, 53, 80, 110, 143, 443, 465, 587, 993, 995, 2082, 2083, 2086, 2087
Mismos puertos cPanel estandar: 2082 (http), 2083 (https), 2086 (WHM http), 2087 (WHM https)
El servidor .133 corre una configuracion casi identica a la de Ecstatic Crafts: mismo cPanel, misma version de OpenSSH, misma estructura de puertos. Aloja esriven.com — probablemente otro cliente de Zuliatec. La instalacion no es unica; es una operacion de hosting con varios servidores. Server .133 runs a nearly identical configuration to Ecstatic Crafts: same cPanel, same OpenSSH version, same port structure. It hosts esriven.com — likely another Zuliatec customer. This isn't a one-off setup; it's a hosting operation with multiple servers.

Prueba 5c: 32 CVEs en el stack subyacente del servidorTest 5c: 32 CVEs in the underlying server stack

Shodan InternetDB reporta 32 vulnerabilidades publicas conocidas afectando el software que corre debajo de WordPress en ecstaticcrafts.com: Shodan InternetDB reports 32 known public vulnerabilities affecting the software running beneath WordPress on ecstaticcrafts.com:

Software afectado:
• OpenSSH 7.4 (EOL 2017 — 9 anos sin parches)
• Exim 4.95 (mail server, multiples RCEs historicas)
• Apache HTTP Server (version ocultada)
• MySQL, Pure-FTPd, cPanel

Ejemplos de CVEs notables:
• CVE-2023-38408 — OpenSSH agent RCE
• CVE-2025-26465 — OpenSSH signature bypass
• CVE-2022-37451 — Exim heap overflow
• CVE-2023-51766 — Exim SMTP smuggling
• CVE-2020-15778 — OpenSSH SCP command injection
Estas son vulnerabilidades del sistema operativo y los servicios, no de WordPress. Un atacante que explote OpenSSH o el servidor de correo gana acceso a toda la maquina — y desde ahi, al sitio. El unico camino es que el proveedor actualice la infraestructura, o que el sitio se mueva a otro proveedor. These are operating system and service vulnerabilities, not WordPress ones. An attacker who exploits OpenSSH or the mail server gains access to the whole machine — and from there, to the site. The only path is for the provider to update the infrastructure, or for the site to move.

Prueba 6: Servidores DNS en el mismo IP que el sitioTest 6: DNS servers on the same IP as the website

ecstaticcrafts.com NS records:
ns1.ecstaticcrafts.com → 67.23.63.136
ns2.ecstaticcrafts.com → 67.23.63.136
ecstaticcrafts.com A record:
ecstaticcrafts.com → 67.23.63.136 (mismo IP)
Los dos servidores DNS (ns1 y ns2) que responden consultas sobre el dominio estan en el mismo IP — literalmente la misma maquina que el sitio web. No hay redundancia. En hosting serio, ns1 y ns2 viven en maquinas separadas en data centers separados. Aqui viven en el mismo disco. Si la maquina cae, cae todo. The two DNS servers (ns1 and ns2) that answer queries about the domain are at the same IP — literally the same machine as the website. No redundancy. On serious hosting, ns1 and ns2 live on separate machines in separate data centers. Here they share a disk. If the machine goes down, everything goes down.

Consecuencias practicasPractical consequences

ProblemaProblem Impacto en Ecstatic CraftsImpact on Ecstatic Crafts
Recursos compartidosShared resourcesCuando otro sitio en el mismo servidor recibe trafico, el rendimiento de ecstaticcrafts.com baja automaticamente. Observado: TTFB variando de 1.17s a 2.93s hoy (vs 1.2s ayer).When another site on the same server gets traffic, ecstaticcrafts.com performance automatically drops. Observed: TTFB varying from 1.17s to 2.93s today (vs 1.2s yesterday).
Sin redundancia DNSNo DNS redundancyUn solo punto de fallo. Si el servidor cae, DNS cae, email cae, sitio cae — todo simultaneamente. Sin failover.Single point of failure. If the server goes down, DNS goes down, email goes down, site goes down — all simultaneously. No failover.
Conflicto de interesConflict of interestEl desarrollador (Zuliatec) es tambien el proveedor de hosting. Cualquier queja sobre velocidad va al mismo lugar que decidio cuanto hardware asignar.The developer (Zuliatec) is also the hosting provider. Any speed complaint goes to the same party that decided how much hardware to allocate.
Sin control de la infraestructuraNo infrastructure controlPara habilitar mod_headers o deshabilitar TLS 1.1 — hay que pedirle al proveedor. Esto explica por que los encabezados de seguridad S3 no aparecen aunque el .htaccess esta correcto: el modulo necesario no esta habilitado en el servidor.To enable mod_headers or disable TLS 1.1 — you have to ask the provider. This explains why security headers from S3 don't appear despite correct .htaccess rules: the required module is not enabled on the server.
Conclusion: El piso de ~1.2s TTFB es el limite fisico de esta configuracion. El software ya esta optimizado al maximo. La siguiente mejora requiere infraestructura — Cloudflare o migracion. En paralelo, hay riesgo operacional: un solo servidor, sin redundancia DNS, gestionado por una agencia pequena sin SLA formal. Conclusion: The ~1.2s TTFB floor is the physical limit of this setup. Software is already fully optimized. The next improvement requires infrastructure — Cloudflare or migration. In parallel, there's operational risk: one server, no DNS redundancy, managed by a small agency without a formal SLA.

Solucion 1: Cloudflare CDN (gratis — requiere cambio de DNS)Solution 1: Cloudflare CDN (free — requires DNS change)

Impacto estimado: TTFB de 1.3s a ~200msEstimated impact: TTFB from 1.3s to ~200ms

Maximo impactoHighest impact

Cloudflare actua como un intermediario entre los visitantes y el servidor. Copia las paginas, imagenes, CSS y JavaScript a mas de 300 servidores alrededor del mundo. Cuando alguien visita el sitio, Cloudflare le entrega la copia mas cercana en vez de ir hasta el servidor original. Cloudflare acts as an intermediary between visitors and the server. It copies pages, images, CSS, and JavaScript to 300+ servers around the world. When someone visits the site, Cloudflare delivers the nearest copy instead of going all the way to the origin server.

En vez de que todos los clientes vayan a la unica tienda que existe, Cloudflare pone copias de tu tienda en cada ciudad del mundo. El cliente siempre entra a la mas cercana. Resultado: la pagina carga en ~200ms en vez de 1.3 segundos. Es gratis. Se configura en 15 minutos. Instead of all customers going to the single store that exists, Cloudflare puts copies of your store in every city in the world. The customer always enters the nearest one. Result: the page loads in ~200ms instead of 1.3 seconds. It's free. Set up takes 15 minutes.

Que se necesita: cambiar los nameservers del dominio ecstaticcrafts.com en el registrador (donde se compro el dominio) a los nameservers de Cloudflare. Esto no afecta el email ni ningun otro servicio — solo cambia quien resuelve el DNS. What's needed: change the nameservers for ecstaticcrafts.com at the registrar (where the domain was purchased) to Cloudflare's nameservers. This doesn't affect email or any other service — it only changes who resolves DNS.

Ademas resuelve automaticamente:Also automatically fixes:

HTTP/2 y HTTP/3HTTP/2 and HTTP/3Activado automaticamente — los 59 scripts cargan en paralelo en vez de en colaAutomatically enabled — 59 scripts load in parallel instead of queuing
TLS 1.1Un toggle en Cloudflare: Version minima TLS → 1.2. Resuelve el hallazgo S1.One toggle in Cloudflare: Minimum TLS Version → 1.2. Fixes finding S1.
Encabezados de seguridadSecurity headersCloudflare puede inyectar HSTS, X-Frame-Options, etc. sin depender de mod_headers del servidorCloudflare can inject HSTS, X-Frame-Options, etc. without depending on server mod_headers
Compresion BrotliBrotli compressionMas eficiente que gzip — reduce el tamano de transferencia ~15-20% adicionalMore efficient than gzip — reduces transfer size ~15-20% more
Proteccion DDoSDDoS protectionIncluido gratis — protege contra ataques de denegacion de servicioIncluded free — protects against denial of service attacks

Solucion 2: Migrar hosting (si Cloudflare no es suficiente)Solution 2: Migrate hosting (if Cloudflare isn't enough)

Impacto estimado: TTFB de 1.3s a ~100-300ms (sin CDN)Estimated impact: TTFB from 1.3s to ~100-300ms (without CDN)

Alto impactoHigh impact

El servidor actual (IP 67.23.63.136, nameservers propios ns1/ns2.ecstaticcrafts.com) es probablemente hosting compartido en un solo servidor. Incluso archivos HTML estaticos toman 1.3s en ser entregados. Un hosting administrado de WordPress usa infraestructura optimizada especificamente para WordPress + WooCommerce. The current server (IP 67.23.63.136, self-hosted nameservers ns1/ns2.ecstaticcrafts.com) is likely shared hosting on a single box. Even static HTML files take 1.3s to deliver. Managed WordPress hosting uses infrastructure specifically optimized for WordPress + WooCommerce.

Host PrecioPrice Por queWhy
Cloudways~$14/moMejor relacion costo/rendimiento. DigitalOcean o Vultr. Varnish + CDN incluido.Best cost/performance ratio. DigitalOcean or Vultr. Varnish + CDN included.
SiteGround GoGeek~$15/moBuen soporte WooCommerce, CDN y cache incluidos.Good WooCommerce support, CDN and caching included.
Kinsta~$35/moPremium. Infraestructura Google Cloud. CDN incluido.Premium. Google Cloud infrastructure. CDN included.
WP Engine~$25/moPlanes optimizados para WooCommerce.WooCommerce-optimized plans.

Migracion: Se instala el plugin "All-in-One WP Migration" o "Duplicator", se exporta el sitio, se importa en el nuevo hosting, y se actualizan los nameservers. Tarda aproximadamente 1 hora. Migration: Install the "All-in-One WP Migration" or "Duplicator" plugin, export the site, import on the new host, and update nameservers. Takes approximately 1 hour.

Solucion 3: Reducir scripts con Asset CleanUp (WP admin)Solution 3: Reduce scripts with Asset CleanUp (WP admin)

Impacto estimado: 59 scripts a ~25Estimated impact: 59 scripts to ~25

Medio impactoMedium impact

Instalar el plugin gratuito "Asset CleanUp: Page Speed Booster" y descargar scripts de WooCommerce, FormCraft, YITH Wishlist y Variation Swatches en paginas que no los necesitan (homepage, blog, about). Esto no mejora el TTFB pero reduce el tiempo de renderizado despues de que el HTML llega al navegador. Install the free "Asset CleanUp: Page Speed Booster" plugin and unload WooCommerce, FormCraft, YITH Wishlist, and Variation Swatches scripts on pages that don't need them (homepage, blog, about). This won't improve TTFB but reduces render time after the HTML reaches the browser.

Items de seguridad pendientes (requieren acceso al servidor)Pending security items (require server access)

ItemItem Que se necesitaWhat's needed
Eliminar log de MerlinDelete Merlin logAcceso FTP/cPanel: eliminar /wp-content/uploads/merlin-wp/ completoFTP/cPanel access: delete /wp-content/uploads/merlin-wp/ entirely
Desactivar wp-cron.phpDisable wp-cron.phpAgregar define('DISABLE_WP_CRON', true); a wp-config.php + crear cron del servidorAdd define('DISABLE_WP_CRON', true); to wp-config.php + set up server cron
Encabezados de seguridadSecurity headersLas reglas .htaccess se agregaron pero mod_headers no esta habilitado en el servidor. Requiere que el proveedor de hosting lo active, o usar Cloudflare (que los inyecta a nivel de edge).The .htaccess rules were added but mod_headers is not enabled on the server. Requires the hosting provider to enable it, or use Cloudflare (which injects them at edge level).
TLS 1.1Requiere cambio en la configuracion de Apache (SSLProtocol) — acceso root o cPanel SSL, o Cloudflare.Requires change in Apache config (SSLProtocol) — root or cPanel SSL access, or Cloudflare.

Briefing: la conversacion con el proveedor Briefing: the provider conversation

Material de apoyo para tener una conversacion con Zuliatec. No es acusacion — son hechos verificables y ocho preguntas que cualquier proveedor competente responde en cinco minutos. Supporting material for a conversation with Zuliatec. Not an accusation — just verifiable facts and eight questions any competent provider answers in five minutes.

Lo que se pago vs. lo que se recibioWhat was paid for vs. what was delivered

El sitio estaba operando a menos del 30% de su potencial. Sin tocar el servidor — solo ajustando WordPress — en una sesion de trabajo: The site was operating at less than 30% of its potential. Without touching the server — just adjusting WordPress — in one work session:

MetricaMetric AntesBefore DespuesAfter MejoraImprovement
Tiempo al primer byteTime to first byte3.82s~1.2s69%
Carga totalTotal load time4.5s~1.5s67%
Plugins activosActive plugins~55~2555% menosfewer
Hallazgos de seguridad corregidosSecurity findings fixed010+10

Todos estos cambios son trabajo de WordPress, no de infraestructura. Un proveedor con monitoreo basico los habria identificado o recomendado. El sitio opero a un tercio de su capacidad durante anos. All these changes are WordPress work, not infrastructure. A provider with basic monitoring would have flagged or recommended them. The site ran at a third of its capacity for years.

Los hechos que importanThe facts that matter

1. El servidor es compartido1. The server is shared

No es cloud. No es servidor dedicado. Es hosting compartido (cPanel) en un data center rentado (Telx, Nueva York). En el mismo bloque IP hay 7 otros servidores, administrados por distintos propietarios, con distintos niveles de mantenimiento. Uno de ellos es un aparato VoIP ruso de 2007 con certificado SSL vencido en 2017 y mas de 180 vulnerabilidades sin parchar. Nadie lo ha tocado en nueve anos. La ausencia de inventario y auditoria del vecindario dice mas del proveedor que cualquier adjetivo. It's not cloud. It's not dedicated. It's cPanel shared hosting in a rented data center (Telx, New York). Seven other servers share the same IP block, owned by different parties with different maintenance levels. One of them is a Russian VoIP appliance from 2007 with an SSL certificate that expired in 2017 and 180+ unpatched vulnerabilities. Nobody has touched it in nine years. The absence of inventory and neighborhood auditing says more about the provider than any adjective could.

2. El proveedor no ha optimizado su propio sitio2. The provider has not optimized their own site

zuliatec.com tarda 3.01 segundos en responder — la misma franja donde estaba ecstaticcrafts.com antes de la optimizacion (3.82s). Es poco convincente como carta de presentacion de una empresa que vende hosting. zuliatec.com takes 3.01 seconds to respond — the same range ecstaticcrafts.com was in before optimization (3.82s). Not a convincing calling card for a company that sells hosting.

3. El desarrollador es el hosting3. The developer is the hosting

Zuliatec es a la vez la agencia que construyo el sitio y el proveedor del servidor donde vive. No es mala fe — es comun en agencias pequenas — pero significa que no hay un tercero que distinga entre "el sitio es lento" y "el hosting es lento". Las quejas llegan al mismo escritorio que decide cuanto hardware asignar. Zuliatec is both the agency that built the site and the provider of the server it lives on. Not bad faith — common in small agencies — but it means no third party can distinguish "the site is slow" from "the hosting is slow". Complaints arrive at the same desk that decides how much hardware to allocate.

4. Sin redundancia4. No redundancy

DNS, web y correo viven en la misma maquina con el mismo IP (67.23.63.136). Los dos "servidores" DNS (ns1 y ns2) son la misma IP. Si la maquina cae: se cae el sitio, se cae el correo, se cae la resolucion del dominio — todo a la vez. Es la configuracion mas barata posible, y se nota. DNS, web, and mail all live on the same machine with the same IP (67.23.63.136). The two "DNS servers" (ns1 and ns2) are the same IP. If the machine goes down: site down, email down, domain resolution down — all at once. It's the cheapest possible setup, and it shows.

5. Modulos de seguridad estandar sin habilitar5. Standard security modules not enabled

Escribimos reglas .htaccess para agregar encabezados de seguridad HTTP (HSTS, X-Frame-Options, CSP). No funcionaron porque el modulo mod_headers de Apache no esta habilitado en el servidor. Habilitarlo es una linea de configuracion. Un hosting serio lo trae activo por defecto desde hace mas de una decada. We wrote .htaccess rules to add HTTP security headers (HSTS, X-Frame-Options, CSP). They don't work because the Apache mod_headers module is not enabled on the server. Enabling it is a one-line config change. Serious hosting has had this on by default for over a decade.

Ocho preguntas para ZuliatecEight questions for Zuliatec

Preguntas que cualquier proveedor competente responde en cinco minutos. Las respuestas — o los silencios — son la informacion. Questions any competent provider can answer in five minutes. The answers — or the silences — are the information.

  1. Cuantos sitios comparten servidor con nosotros?How many sites share the server with us?
    Una respuesta honesta es un numero. "No se" o "muchos" es una mala senal.An honest answer is a number. "I don't know" or "many" is a bad sign.
  2. Cual es su cadencia de parches para Apache, OpenSSH, PHP y cPanel?What's your patching cadence for Apache, OpenSSH, PHP, and cPanel?
    Un proveedor serio dice "semanalmente" o "automatico cada N dias". "Cuando hay problemas" significa que no hay. Hay 32 CVEs abiertos en el stack subyacente — no son hipoteticos.A serious provider says "weekly" or "automatic every N days". "When there are problems" means there isn't one. There are 32 open CVEs in the underlying stack — not hypothetical.
  3. Pueden habilitar mod_headers en Apache?Can you enable mod_headers in Apache?
    Un comando. Treinta segundos. Si la respuesta es "no podemos" o "es complicado", el servidor no se administra con atencion al detalle.One command. Thirty seconds. If the answer is "we can't" or "it's complicated", the server isn't managed with attention to detail.
  4. Por que sigue activo TLS 1.1?Why is TLS 1.1 still active?
    PCI DSS exige TLS 1.2 minimo desde 2018 para cualquier sitio que procese pagos. Es una tienda WooCommerce. La pregunta tiene peso regulatorio.PCI DSS has required TLS 1.2 minimum since 2018 for any site processing payments. It's a WooCommerce store. The question has regulatory weight.
  5. Tienen SLA escrito con garantia de uptime?Do you have a written SLA with uptime guarantee?
    Sin SLA escrito, no hay compromiso. Y tampoco hay recurso cuando las cosas fallan.No written SLA means no commitment. And no recourse when things break.
  6. Cual es el plan de recuperacion de desastres?What's the disaster recovery plan?
    La respuesta concreta suena asi: "backup cada X horas, restauracion en Y minutos, probado mensualmente". Cualquier cosa mas vaga significa que no hay plan.A concrete answer sounds like: "backup every X hours, restoration in Y minutes, tested monthly". Anything vaguer means no plan.
  7. Pueden migrarnos a un plan con mas recursos?Can you move us to a plan with more resources?
    Pregunta directa que no rompe la relacion. Si ofrecen un tier superior con precios claros, entienden el problema. Si no tienen opciones superiores, saben el tamano real del negocio.A direct question that doesn't break the relationship. If they offer a higher tier with clear pricing, they understand the problem. If they have no higher options, you know the actual size of the business.
  8. Podemos separar el DNS del hosting?Can we separate DNS from the hosting?
    Mover el DNS a Cloudflare (gratis) elimina el punto unico de fallo sin cambiar el hosting. Un proveedor tranquilo lo permite sin friccion. Resistencia aqui es una bandera.Moving DNS to Cloudflare (free) removes the single point of failure without changing hosting. A relaxed provider allows it without friction. Resistance here is a flag.

Marco de decisionDecision framework

OpcionOption Cuando tiene sentidoWhen it makes sense CostoCost
A · Quedarse y presionarStay and pressure Si Zuliatec responde bien las preguntas, ofrece un plan superior y se compromete con mejoras en tiempo.If Zuliatec answers the questions well, offers a higher tier, and commits to time-bound improvements. $0
B · Quedarse + CloudflareStay + Cloudflare Cloudflare gratis resuelve ~80% de lo residual (velocidad, TLS 1.1, encabezados de seguridad) sin tocar la relacion con Zuliatec.Free Cloudflare handles ~80% of residual issues (speed, TLS 1.1, security headers) without touching the Zuliatec relationship. $0
C · Separar DNSSeparate DNS Mueve el DNS a Cloudflare sin migrar el hosting. Elimina el punto unico de fallo y da respaldo si el servidor cae.Move DNS to Cloudflare without migrating the hosting. Removes the single point of failure and provides fallback if the server dies. $0
D · Migracion a hosting administradoMigrate to managed hosting Si Zuliatec no responde bien, no tiene tier superior, o no hay SLA. Cloudways (~$14/mes), SiteGround GoGeek (~$15/mes), Kinsta (~$35/mes).If Zuliatec doesn't answer well, has no higher tier, or no SLA. Cloudways (~$14/mo), SiteGround GoGeek (~$15/mo), Kinsta (~$35/mo). $14–35/mesmo

Accion sugeridaSuggested action

AccionAction

Opcion B esta semana: agregar Cloudflare (gratis, 15 minutos, no rompe nada). Resuelve ~80% del problema residual sin tocar el hosting. En paralelo, enviar las ocho preguntas a Zuliatec por escrito con 72 horas de plazo. Las respuestas aclaran si corresponde A o D. Option B this week: add Cloudflare (free, 15 minutes, doesn't break anything). Handles ~80% of residual problems without touching hosting. In parallel, send the eight questions to Zuliatec in writing with a 72-hour deadline. The answers clarify whether A or D is the right path.

El tono puede ser simple: "Contratamos una auditoria externa. Estos son los hallazgos, estas son las preguntas, nos gustaria entender mejor el servicio." Un buen proveedor lo agradece y responde. Uno que se pone defensivo tambien respondio, solo que por otro canal. The tone can be simple: "We hired an external audit. Here are the findings, here are the questions, we'd like to understand the service better." A good provider thanks you and responds. One who gets defensive also responds — just through a different channel.

Fase 3: Pentest de Infraestructura Phase 3: Infrastructure Pentest

Escaneo externo de infraestructura con herramientas especializadas: nmap, subfinder, nuclei, wpscan, wafw00f, sslscan, testssl.sh. Sin explotacion, sin ataques de credenciales. Solo reconocimiento a profundidad. Velocidad limitada a 2-5 req/s para no afectar produccion. External infrastructure scanning with specialized tools: nmap, subfinder, nuclei, wpscan, wafw00f, sslscan, testssl.sh. No exploitation, no credential attacks. Deep reconnaissance only. Throttled at 2-5 req/s to avoid impacting production.

Hallazgo principalHeadline finding

El sitio de WordPress es la fachada. Detras del puerto 443 hay un segundo servidor en el puerto 8000 — una aplicacion Django abandonada con DEBUG=True, Python 3.6 (sin parches desde 2021), y un panel de admin accesible a cualquiera. Es como tener una casa bien cerrada con llave mientras la cochera tiene la puerta abierta de par en par. The WordPress site is the storefront. Behind port 443 there's a second server on port 8000 — an abandoned Django application with DEBUG=True, Python 3.6 (no patches since 2021), and an admin panel accessible to anyone. It's a well-locked house with the garage door wide open.

Puertos abiertosOpen ports
17
Top 1000 escaneadosTop 1000 scanned
SubdominiosSubdomains
10
Misma IP, cero segmentacionSame IP, zero segmentation
TLS
A
Solo TLSv1.2 + 1.3TLSv1.2 + 1.3 only
WAF
NingunoNone
wafw00f: sin deteccionwafw00f: no detection

S11. Django con DEBUG=True en produccion — Python 3.6 EOL S11. Django with DEBUG=True in production — Python 3.6 EOL

CriticoCritical

Puerto 8000 corre una aplicacion Django via gunicorn 20.1.0. DEBUG=True en produccion. Cada error devuelve un traceback completo con rutas del servidor, version de Python, y configuracion del venv. Python 3.6 no recibe parches de seguridad desde diciembre 2021. Port 8000 runs a Django application via gunicorn 20.1.0. DEBUG=True in production. Every error returns a full traceback with server paths, Python version, and venv configuration. Python 3.6 has received no security patches since December 2021.

URL: http://ecstaticcrafts.com:8000/
Response: 500 — NoReverseMatch at /
Python: 3.6 (EOL 2021-12-23)
Server path: /var/www/html/api_ecstatic_crafts/
Platform: Red Hat Software Collections (/opt/rh/rh-python36/)
WSGI: gunicorn 20.1.0
Debug page: 86 KB de informacion filtrada
Es un servidor que ante cualquier pregunta responde con su biografia completa: donde vive, que version de todo tiene, como esta organizado por dentro. Y ademas tiene 4 anos sin vacunas. It's a server that responds to any question with its full biography: where it lives, what version of everything it runs, how it's organized inside. And it hasn't had a security update in 4 years.
Solve → Apagar el servicio gunicorn en el puerto 8000 inmediatamente. Si la API Django no se usa (la app React que la consume esta muerta), no hay razon para que siga corriendo. Si se necesita, DEBUG=False y migrar a Python 3.12+. Shut down the gunicorn service on port 8000 immediately. If the Django API isn't in use (the React app that consumed it is dead), there's no reason for it to keep running. If it's needed, DEBUG=False and migrate to Python 3.12+.

S19. Panel de admin Django accesible publicamente S19. Django admin panel publicly accessible

Alto / High

La pagina de login del admin de Django esta abierta en :8000/admin/. Sin restriccion de IP, sin VPN, sin 2FA. Combinado con S11 (DEBUG=True en el mismo puerto), un atacante puede intentar credenciales y ademas ver la traza completa de cada error. The Django admin login page is open at :8000/admin/. No IP restriction, no VPN, no 2FA. Combined with S11 (DEBUG=True on the same port), an attacker can attempt credentials and also see the full traceback on every error.

URL: http://ecstaticcrafts.com:8000/admin/
Response: 200 — login form (username, password, CSRF token)
Title: "Log in | Django site admin"
Binding: 0.0.0.0:8000 (all interfaces)
La puerta de la oficina administrativa no tiene cerradura. Solo hay que saber que existe — y ahora se sabe. The door to the administrative office has no lock. You just have to know it exists — and now that's known.
Solve → Si el servicio Django se apaga (solucion de S11), esto se resuelve automaticamente. Si debe mantenerse, restringir /admin/ por IP en el firewall o en la configuracion de gunicorn/nginx. If the Django service is shut down (S11 fix), this resolves automatically. If it must stay, restrict /admin/ by IP in the firewall or gunicorn/nginx config.

S13. MySQL (3306) expuesto a internet S13. MySQL (3306) exposed to the internet

Alto / High

MySQL escucha en 0.0.0.0:3306 y responde a conexiones TCP desde cualquier IP. La autenticacion es por whitelist de IP, pero el servicio sigue siendo alcanzable. Revela que la base de datos existe y esta activa. Vulnerable a DoS por inundacion de conexiones y a cualquier bug del protocolo de autenticacion de MySQL. MySQL listens on 0.0.0.0:3306 and responds to TCP connections from any IP. Authentication is IP-whitelist based, but the service is still reachable. Reveals the database exists and is active. Vulnerable to connection-flood DoS and any MySQL authentication protocol bug.

Port: 3306/tcp — open
Response: "Host '217.xxx.xxx.xxx' is not allowed to connect to this MySQL server"
Binding: 0.0.0.0 (all interfaces)
La caja fuerte esta en la acera. Tiene buena cerradura, pero cualquiera puede llegar y probar. Deberia estar adentro del edificio. The safe is on the sidewalk. It has a decent lock, but anyone can walk up and try it. It should be inside the building.
Solve → En my.cnf, configurar bind-address = 127.0.0.1. Si hay aplicaciones remotas que necesitan acceso, usar un tunel SSH o VPN en vez de exponer el puerto directamente. In my.cnf, set bind-address = 127.0.0.1. If remote applications need access, use an SSH tunnel or VPN instead of exposing the port directly.

S10. 10 subdominios en una sola IP — cero segmentacion S10. 10 subdomains on a single IP — zero segmentation

MedioMedium

Todos los subdominios resuelven a la misma IP: 67.23.63.136. WordPress, Django, React, cPanel, DNS, correo — todo en la misma caja. Si algo se compromete, todo se compromete. All subdomains resolve to the same IP: 67.23.63.136. WordPress, Django, React, cPanel, DNS, mail — all on the same box. If anything is compromised, everything is compromised.

api.ecstaticcrafts.com → Django backend (roto, DEBUG=True)
app.ecstaticcrafts.com → React SPA (create-react-app, muerta)
cp.ecstaticcrafts.com → cPanel
mail.ecstaticcrafts.com → webmail
prueba.ecstaticcrafts.com → placeholder
ns1/ns2.ecstaticcrafts.com → nameservers (misma IP)
www.api / www.app / www.prueba → aliases
IP: 67.23.63.136 (Telx, NYC) — todas
Tienda, oficina, correo, bodega, laboratorio abandonado — todo en el mismo edificio con una sola puerta. Los dos "servidores" DNS (ns1 y ns2) son la misma IP. Si se cae uno, se cae todo. Store, office, mail, warehouse, abandoned lab — all in one building with one door. The two "DNS servers" (ns1 and ns2) are the same IP. If one goes down, everything goes down.

S14. Stack de correo completo expuesto S14. Full mail stack exposed

MedioMedium

7 puertos de correo abiertos: SMTP (25 filtrado), POP3 (110), IMAP (143), SMTPS (465), Submission (587), IMAPS (993), POP3S (995). Los banners revelan las versiones: Exim 4.95 y Dovecot. Exim tiene un historial extenso de RCEs criticos. 7 mail ports open: SMTP (25 filtered), POP3 (110), IMAP (143), SMTPS (465), Submission (587), IMAPS (993), POP3S (995). Banners reveal versions: Exim 4.95 and Dovecot. Exim has an extensive history of critical RCEs.

465 SMTPS: Exim 4.95
587 Submission: Exim 4.95
110/143/993/995: Dovecot
CVEs notables: CVE-2019-10149, CVE-2020-28017
Solve → Si el correo se maneja via Google Workspace u otro proveedor externo, estos servicios pueden apagarse en el servidor. Si se usan, actualizar Exim y esconder los banners de version (smtp_banner en la config de Exim). If mail is handled via Google Workspace or another external provider, these services can be shut down on the server. If in use, update Exim and hide version banners (smtp_banner in Exim config).

S15. OpenSSH 7.4 — sin actualizaciones desde 2017 S15. OpenSSH 7.4 — no updates since 2017

MedioMedium

OpenSSH 7.4 fue lanzado en diciembre 2016. RHEL a veces aplica parches de seguridad sin cambiar el numero de version, asi que puede ser menos severo de lo que el numero sugiere. Pero desde afuera no se puede verificar. OpenSSH 7.4 was released December 2016. RHEL sometimes backports security patches without updating the version string, so it may be less severe than the number suggests. But it can't be verified externally.

Version: OpenSSH_7.4 protocol 2.0
Host keys: RSA 2048, ECDSA 256, ED25519 256
EOL: 2017 (upstream)
Solve → Verificar con el proveedor si RHEL ha aplicado backports de seguridad. Si no, actualizar el sistema operativo base (que parece ser RHEL/CentOS 7, tambien en EOL desde junio 2024). Verify with the hosting provider whether RHEL has applied security backports. If not, upgrade the base OS (which appears to be RHEL/CentOS 7, also EOL since June 2024).

S20. Sin WAF detectado S20. No WAF detected

MedioMedium

wafw00f no detecto ningun Web Application Firewall. AIOS funciona como plugin de seguridad de WordPress pero no opera a nivel de solicitudes HTTP — no filtra payloads maliciosos ni limita velocidad a nivel de red. wafw00f detected no Web Application Firewall. AIOS works as a WordPress security plugin but doesn't operate at the HTTP request level — it doesn't filter malicious payloads or rate-limit at the network layer.

Solve → Cloudflare (gratis) resuelve esto: WAF basico, rate limiting, y DDoS protection. Requiere el cambio de nameservers ya mencionado en la seccion de rendimiento. Cloudflare (free tier) solves this: basic WAF, rate limiting, and DDoS protection. Requires the nameserver change already mentioned in the performance section.

S18. Encabezados de seguridad no aplicados en el dominio principal S18. Security headers not applied on main domain

MedioMedium

En Fase 1 reportamos que mod_headers no estaba habilitado (S3). Error nuestro. Los subdominios (api., app.) SI devuelven los encabezados de seguridad que configuramos via .htaccess. El problema es que el dominio principal no los aplica — probablemente WP Rocket o algun plugin los esta descartando. In Phase 1 we reported that mod_headers wasn't enabled (S3). Our mistake. The subdomains (api., app.) DO return the security headers we configured via .htaccess. The issue is the main domain doesn't apply them — likely WP Rocket or some plugin is discarding them.

api.ecstaticcrafts.com: X-Frame-Options, X-Content-Type-Options, Referrer-Policy ✓
ecstaticcrafts.com: none of the above
Solve → Revisar la configuracion de WP Rocket y Asset CleanUp — uno de ellos podria estar limpiando los encabezados HTTP. Otra opcion: mover los encabezados del .htaccess a la configuracion del vhost de Apache, donde ningun plugin puede tocarlos. Review WP Rocket and Asset CleanUp configuration — one of them may be stripping HTTP headers. Alternative: move headers from .htaccess to Apache's vhost config, where no plugin can touch them.

S12. App React abandonada — registro de propiedad de obras de arte con disclosure total del codigo fuente S12. Abandoned React app — art ownership registry with full source code disclosure

CriticoCritical

app.ecstaticcrafts.com sirve una SPA de React 16 (create-react-app) que implementa un registro digital de propiedad de obras de arte: propietarios, artistas, obras, piezas (con codigos bidimensionales/QR), series, y un flujo de transferencia de propiedad entre coleccionistas. Aunque el backend esta roto (ver S11), la SPA sigue siendo publica y los source maps estan expuestos, lo que permite reconstruir el codigo fuente completo del frontend — 685 archivos originales, incluyendo 23 endpoints del API, la logica de autenticacion, y los nombres internos de los casos de uso. app.ecstaticcrafts.com serves a React 16 SPA (create-react-app) implementing a digital art ownership registry: owners, artists, works, pieces (with 2D/QR codes), series, and an ownership-transfer workflow between collectors. Even though the backend is broken (see S11), the SPA is still public and its source maps are exposed, which lets an attacker reconstruct the entire frontend source — 685 original files including 23 API endpoints, authentication logic, and internal use-case names.

URL: https://app.ecstaticcrafts.com/
Bundle: main.3d23b254.js (1.16 MB), main.22cf3f87.css (288 KB), 787.077ef86b.chunk.js (4.6 KB)
API esperado: https://api.ecstaticcrafts.com/api → 500 en todos los endpoints (ver S11)
Arquitectura: 22 rutas, 8 modulos (CU0-Login, CU1-Propietarios, CU2-Artes+Piezas, CU3-Artistas, CU4-Registro+Ver, CU4.1-MisArtes, CU4.2-Transferencia, CU5-Series)
Actores: grupo_actor=1 (admin), grupo_actor=2 (propietario/coleccionista)
Idiomas: 8 (es, en, fr, it, de, pt, ar, hi)
Sub-hallazgos: S21 (source maps), S22 (localStorage), S23 (sin SRI), S24 (API disclosure), S25 (asset-manifest), S26 (stack EOL)
Es un archivo digital para registrar quien es dueño de cada obra de arte, con un sistema para transferir la propiedad de un coleccionista a otro. Aunque el motor (backend) dejo de funcionar hace tiempo, el edificio sigue abierto al publico con los planos completos pegados en la pared. Cualquiera puede entrar, leer todo, y memorizar exactamente como funciona — para cuando vuelvan a encender el motor, ya sabrian como operar cada boton. It's a digital ledger for recording who owns which art piece, with a system to transfer ownership between collectors. Even though the engine (backend) stopped working a while ago, the building is still open to the public with the full blueprints taped to the wall. Anyone can walk in, read everything, and memorize exactly how it works — so when the engine is turned back on, they already know which buttons do what.
Solve → En orden de preferencia: (1) si la app esta realmente abandonada, desmontar el vhost de Apache para app. y api., eliminar los subdominios del DNS. Esto elimina S12, S21–S26 de un solo golpe. (2) Si se planea reactivar, primero: regenerar el build sin source maps (GENERATE_SOURCEMAP=false), mover la sesion de localStorage a cookies httpOnly, agregar integrity= a los scripts de unpkg (o mejor, auto-hostear), migrar a React 18+, y auditar el endpoint /post_send_email y /put_transferir_arte antes de re-exponerlos. In order of preference: (1) if the app is truly abandoned, tear down the Apache vhost for app. and api. and remove the DNS records. This clears S12 and S21–S26 in one move. (2) If reactivation is planned, first: rebuild with no source maps (GENERATE_SOURCEMAP=false), move session from localStorage to httpOnly cookies, add integrity= on the unpkg scripts (or better, self-host them), migrate to React 18+, and audit /post_send_email and /put_transferir_arte before re-exposing them.

S21. Source maps del React sirviendose publicamente — disclosure total del codigo cliente S21. React source maps publicly served — full client-side source disclosure

CriticoCritical

Los tres archivos .map de la SPA estan disponibles publicamente con 200 OK y contienen sourcesContent — es decir, cada archivo original .js, .tsx, CSS y SVG esta embebido textualmente. 685 archivos originales recuperados. Esto convierte un reconocimiento externo en un analisis cuasi-whitebox en cinco minutos. All three .map files for the SPA return 200 OK and contain sourcesContent — i.e., every original .js, .tsx, CSS and SVG source file is embedded verbatim. 685 original source paths recovered. This turns external recon into near-whitebox analysis in five minutes.

main.3d23b254.js.map → 200, 3,966,656 bytes (3.97 MB)
main.22cf3f87.css.map → 200, 659,388 bytes
787.077ef86b.chunk.js.map → 200, 10,281 bytes
Archivos fuente: 495 .js, 137 .tsx, 33 .svg, 5 .ts (685 total)
Incluye: nombres de variables, comentarios del desarrollador, logica de auth, keys de traduccion, estructura de modulos
Los source maps son como el plano detallado de una casa: los usan los desarrolladores para depurar errores mientras construyen. En produccion no deberian existir. Aqui estan publicos — cualquiera con un navegador ve los planos completos, incluyendo comentarios del tipo "aqui hay un bug por arreglar". Source maps are like the detailed blueprints of a house: developers use them to debug errors while building. They should not exist in production. Here they're public — anyone with a browser can see the full blueprints, including "there's a bug to fix here" comments.
Solve → Recompilar con GENERATE_SOURCEMAP=false antes de desplegar, o bloquear a nivel de Apache: <FilesMatch "\.map$"> Require all denied </FilesMatch>. Rebuild with GENERATE_SOURCEMAP=false before deployment, or block at Apache: <FilesMatch "\.map$"> Require all denied </FilesMatch>.

S22. Sesion almacenada en localStorage — XSS equivale a robo de sesion S22. Session stored in localStorage — XSS equals session hijack

AltoHigh

El objeto de sesion (user, tipoActor, remember_me) se lee y escribe directamente en window.localStorage y window.sessionStorage. No se usan cookies httpOnly. Cualquier XSS en el dominio — o un compromiso del CDN externo (ver S23) — puede exfiltrar la sesion en una linea de JavaScript. Como la app maneja transferencias de propiedad de obras de arte, el robo de sesion equivale a una transferencia no autorizada. The session object (user, tipoActor, remember_me) is read and written directly to window.localStorage and window.sessionStorage. No httpOnly cookies are used. Any XSS on the domain — or a compromise of the external CDN (see S23) — can exfiltrate the session in one line of JavaScript. Since the app handles art ownership transfers, session hijack equals unauthorized transfer.

Patron (App.js lineas 43-49):
localStorage.getItem("remember_me")
sessionStorage.getItem("user") / localStorage.getItem("user")
sessionStorage.getItem("tipoActor") / localStorage.getItem("tipoActor")
Cookies httpOnly: ninguna / none
La llave de la casa esta debajo de la alfombra donde cualquier invitado que entre por error puede recogerla. La forma correcta es que el portero la guarde en su bolsillo y solo te deje pasar cuando le muestres la cara (cookies httpOnly). The house key is under the mat where any guest who steps in by mistake can pick it up. The right way is for the doorkeeper to keep it in his pocket and only let you in when you show your face (httpOnly cookies).
Solve → Cuando la app se reactive, emitir la sesion desde el backend Django como cookie HttpOnly; Secure; SameSite=Lax. En localStorage dejar solo preferencias no sensibles (idioma, estado de UI). When the app is reactivated, issue the session from the Django backend as an HttpOnly; Secure; SameSite=Lax cookie. Leave only non-sensitive preferences in localStorage (language, UI state).

S23. React, React-DOM y React-Bootstrap cargados desde unpkg.com sin Subresource Integrity S23. React, React-DOM and React-Bootstrap loaded from unpkg.com with no Subresource Integrity

AltoHigh

El index.html carga las tres librerias principales desde unpkg.com con crossorigin pero sin integrity=. Si unpkg se compromete, o si un atacante intercepta la conexion, se ejecuta JavaScript arbitrario con todos los privilegios de la app — incluyendo acceso a los tokens de sesion en localStorage (S22). El tag @next en react-bootstrap es especialmente fragil: flota a versiones pre-release sin notificacion. index.html loads all three core libraries from unpkg.com with crossorigin but without integrity=. If unpkg is compromised, or an attacker MITMs the connection, arbitrary JavaScript executes with full app privileges — including access to the session tokens in localStorage (S22). The @next tag on react-bootstrap is especially fragile: it floats to pre-release versions without notice.

<script src="https://unpkg.com/react/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js" crossorigin></script>
Atributo integrity=: ausente en los tres
Solve → Auto-hostear las librerias (que es lo que create-react-app hace por defecto — la carga desde CDN aqui parece ser una edicion manual del template). Si se mantiene el CDN, anclar versiones exactas y agregar hashes integrity= SHA-384. Self-host the libraries (which is what create-react-app does by default — the CDN loads here look like a hand-edit to the template). If the CDN stays, pin exact versions and add SHA-384 integrity= hashes.

S24. Superficie del backend API revelada — 23 endpoints mapeados sin tocar el servidor S24. Backend API surface disclosed — 23 endpoints mapped without touching the server

AltoHigh

Cada llamada axios es recuperable del source map. La superficie del backend esta completamente mapeada sin necesidad de tocar api.ecstaticcrafts.com. Los endpoints notables para revisar cuando se restaure el backend: /get_env_variables (devuelve configuracion de entorno al cliente — patron clasico de disclosure accidental), /post_send_email (acepta HTML arbitrario y lista de destinatarios — posible open relay de correo si la autenticacion es debil), /put_transferir_arte (la transferencia de propiedad — la integridad del registro depende de la logica de autorizacion aqui). Every axios call is recoverable from the source map. The backend surface is fully mapped without touching api.ecstaticcrafts.com. Notable endpoints to review when the backend is restored: /get_env_variables (returns environment config to the client — classic accidental-disclosure pattern), /post_send_email (accepts arbitrary HTML and recipient list — possible open mail relay if authentication is weak), /put_transferir_arte (the ownership transfer — registry integrity depends on the authorization logic here).

GET (11): /get_accesos_tipo_actor, /get_artes, /get_artistas, /get_piezas, /get_propietarios, /get_propietario_piezas, /get_series, /get_pais_dominio, /get_env_variables, /get_idiomas_dominio, /get_idioma_llaves
POST (2): /post_registro_arte, /post_send_email
PUT (9): /put_arte, /put_artista, /put_pieza, /put_propietario, /put_propietario_pieza, /put_serie, /put_transferir_arte, /put_auth_user, /put_user
DELETE (3): /delete_arte, /delete_artista, /delete_pieza
Solve → Tratar el disclosure como un hecho consumado. Cuando el backend se restaure, cada endpoint debe tener checks explicitos de autorizacion server-side — la seguridad no puede depender de que "nadie sabe los nombres de los endpoints". Treat the disclosure as fait accompli. When the backend is restored, every endpoint must have explicit server-side authorization checks — security cannot depend on "no one knows the endpoint names".

S25. /asset-manifest.json publicamente servido — indice de todos los assets S25. /asset-manifest.json publicly served — index of every static asset

MedioMedium

Responde 4.8 KB de JSON listando cada ruta de asset estatico incluyendo los tres source maps y todos los iconos SVG. Elimina la necesidad de adivinar los nombres con hash de los bundles. Returns 4.8 KB of JSON listing every static asset path including the three source maps and every SVG icon. Removes the need to guess hash-suffixed bundle names.

URL: https://app.ecstaticcrafts.com/asset-manifest.json
Tamano: 4,795 bytes, 200 OK
Contenido: 44 entradas, incluyendo los 3 .map files y 33 SVG icons
Solve → Bloquear con el mismo FilesMatch de S21 o omitir del build. Misma solucion que S21 lo resuelve tambien. Block with the same FilesMatch from S21 or omit from the build. Same fix as S21 resolves it too.

S26. Stack del frontend en End-of-Life — React 16 + create-react-app S26. Frontend stack End-of-Life — React 16 + create-react-app

MedioMedium

El bundle esta construido con React 16 (lanzado en 2017, soporte oficial terminado en 2022 cuando salio React 18) y create-react-app (deprecated oficialmente upstream en 2023). Ambos sin mantenimiento. Sin parches de seguridad upstream. Las dependencias transitivas en node_modules estan fijadas a versiones de 2021 (@coreui/react, react-router, axios, styled-components, date-fns) con CVEs publicas visibles. The bundle is built with React 16 (released 2017, official support ended 2022 when React 18 shipped) and create-react-app (officially deprecated upstream in 2023). Both unmaintained. No upstream security patches. Transitive dependencies in node_modules are pinned to 2021-era versions (@coreui/react, react-router, axios, styled-components, date-fns) with published CVEs visible.

Solve → Si la app se mantiene, migrar a React 18+ sobre Vite o Next.js. Si se archiva, ver la opcion (1) del fix de S12 (desmantelar). If the app is kept, migrate to React 18+ on Vite or Next.js. If shelved, see option (1) of the S12 fix (decommission).

Revision a nivel de codigo — App de registro de arte Code-level review — Art registry app

Con los source maps publicos (S21) extrajimos los 685 archivos fuente originales del bundle. 108 son codigo especifico de Ecstatic Crafts (el resto son node_modules). Revision manual linea-por-linea de las rutas sensibles (login, cambio de clave, transferencia, registro de arte). Aqui los hallazgos concretos — con referencias a archivo y numero de linea. Cada uno es explotable ahora (contra la logica del cliente) o lo sera cuando el backend Django vuelva a funcionar. Por claridad los agrupamos en 5 categorias: A) autenticacion/autorizacion, B) oraculos de enumeracion, C) confianza indebida en el cliente, D) facilitadores de phishing/spam, E) defaults criptograficos/diseño. With source maps public (S21) we extracted the 685 original source files from the bundle. 108 are Ecstatic Crafts-specific code (rest is node_modules). Manual line-by-line review of sensitive paths (login, change password, transfer, art registration). Below are the concrete findings — with file and line references. Each one is exploitable now (against client logic) or will be when the Django backend is restored. For clarity grouped into 5 categories: A) authentication/authorization, B) enumeration oracles, C) server-trust-client, D) phishing/spam enablers, E) cryptographic/design defaults.

A. Autenticacion y Autorizacion A. Authentication & Authorization

SG-1. El token de recuperacion de contraseña es un token de sesion normal SG-1. Password-reset token is just a regular session token

CriticoCritical

El flujo de "cambiar contraseña" lee un ?token=XYZ de la URL y lo envia como Authorization: Token XYZ a PUT /put_user con la nueva clave. No hay verificacion de clave actual, no hay discriminacion entre tipos de token (reset vs sesion), no hay requisitos de complejidad (solo valida que los dos campos esten llenos y coincidan). Cualquier token de sesion robado sirve como token de reset. Cualquier token de reset filtrado otorga sesion permanente. The "change password" flow reads ?token=XYZ from the URL and sends it as Authorization: Token XYZ to PUT /put_user with the new password. No current-password check, no token-type discrimination (reset vs session), no complexity requirements (only validates both fields non-empty and matching). Any stolen session token works as a reset token. Any leaked reset token grants permanent session.

File: components/CU0-Login/ChangePassword/index.js:102-112, 148-159
api.put("/put_user", { clave: formFields.password }, { headers: { Authorization: tok }})
Validacion de clave: solo "no vacio" + "campos coinciden" (lineas 54-78)
No requiere: clave actual, longitud minima, complejidad
La llave que te dan para recuperar la clave es la MISMA llave que abre la puerta principal — y dura para siempre. Si se te pierde un email viejo de recuperacion, el que lo encuentre entra directo. The key they give you to recover your password is the SAME key that opens the front door — and it lasts forever. If an old recovery email leaks, whoever finds it walks right in.
Solve → Usar tokens de reset separados de los de sesion, con TTL corto (15 min) y single-use. Requerir clave actual en el endpoint autenticado. Agregar validacion de complejidad (longitud, mezcla de caracteres). Use reset tokens separate from session tokens, with short TTL (15 min) and single-use. Require current password on the authenticated endpoint. Add complexity validation (length, character mix).

SG-2. El token de reset queda en la URL hasta que React monta SG-2. Reset token stays in the URL until React mounts

AltoHigh

El window.history.pushState que limpia el ?token= de la URL corre dentro de un useEffect, o sea despues del primer render y despues de cargar los scripts externos de unpkg. El token queda en: logs de acceso de Apache, historial del navegador, URL de cualquier extension que observe pestañas. La cabecera Referrer-Policy protege contra fugas cross-origin, pero no contra logs ni historial. The window.history.pushState that strips ?token= from the URL runs inside a useEffect — i.e., after initial render and after external unpkg scripts load. The token ends up in: Apache access logs, browser history, URL visible to any tab-reading extension. The Referrer-Policy header protects cross-origin leaks but not logs or history.

File: components/CU0-Login/ChangePassword/index.js:148-159
useEffect(() => { ...query.get("token")... window.history.pushState(...) }, [])
Solve → Usar replaceState sincronicamente al cargar la pagina (antes del useEffect), o mover el token a un cuerpo POST via formulario auto-submit al llegar al enlace. Use replaceState synchronously on page load (before useEffect), or move the token to a POST body via auto-submit form when the link lands.

SG-3. Tokens DRF sin expiracion ni rotacion SG-3. DRF tokens never expire or rotate

AltoHigh

Cada llamada usa Authorization: Token ${user?.token}. Este es el patron por defecto de TokenAuthentication de Django REST Framework: un token por usuario, sin expiracion, sin rotacion, sin vinculacion a dispositivo. Combinado con localStorage (S22): roban el token una vez, acceden para siempre. Every call uses Authorization: Token ${user?.token}. This is Django REST Framework's default TokenAuthentication pattern: one token per user, no expiration, no rotation, no device binding. Combined with localStorage (S22): steal the token once, access forever.

Archivos: components/CU4.2-TransferirArte/index.js:28, y todos los componentes con llamadas autenticadas
const config = { headers: { Authorization: `Token ${user?.token}` }};
Solve → Migrar a JWT con TTL (15 min access token + 7 dias refresh), o al menos implementar expiring-token de DRF y rotacion en cada login. Bindear al User-Agent o IP si es viable. Migrate to JWT with TTL (15 min access + 7-day refresh), or at minimum implement DRF's expiring-token and rotation on every login. Bind to User-Agent or IP if feasible.

SG-4. El login confia en el rol declarado por el cliente como query param SG-4. Login trusts the client-declared role as a query param

AltoHigh

Despues del PUT /put_auth_user, el frontend llama GET /get_accesos_tipo_actor?grupo_actor=${u.grupo_actor}. El grupo_actor viene del response del login — pero se pasa como parametro de query en la siguiente llamada. Si el backend mira el query en vez de derivar del token: cualquier usuario autenticado puede pedir los permisos del grupo admin cambiando el parametro. Requiere verificacion server-side; el patron del cliente es el indicio. After PUT /put_auth_user, the frontend calls GET /get_accesos_tipo_actor?grupo_actor=${u.grupo_actor}. The grupo_actor comes from the login response — but it's passed as a query parameter on the next call. If the backend looks at the query rather than deriving from the token: any authenticated user can request admin-group permissions by tampering the param. Requires server-side verification; the client pattern is the hint.

File: components/CU0-Login/index.js:212-220
api.get("/get_accesos_tipo_actor", { params: { grupo_actor: u.grupo_actor }, headers: { Authorization: tok }})
Solve → En el backend, ignorar el query param y leer grupo_actor desde el usuario resuelto por el token. Probar manualmente cambiando el param con DevTools para verificar. In the backend, ignore the query param and read grupo_actor from the user resolved by the token. Test manually by tampering the param in DevTools to verify.

SG-5. El flujo de admin se decide por location.state (client-set) SG-5. Admin flow decided by location.state (client-set)

AltoHigh

En la transferencia de arte: if (!locationData?.admin) { putTransferirArte() } else { putPropietario() }. La accion "admin" (putPropietario, que edita perfiles de otros propietarios) se dispara basado en estado de navegacion del cliente — editable en DevTools. Requiere verificacion server-side del rol en cada endpoint sensible. In the art transfer flow: if (!locationData?.admin) { putTransferirArte() } else { putPropietario() }. The admin action (putPropietario, editing other owners' profiles) is triggered based on client-held navigation state — editable in DevTools. Requires server-side role verification on every sensitive endpoint.

File: components/CU4.2-TransferirArte/index.js:91-96, 794-804
Solve → Cada endpoint admin-only (/put_propietario, /delete_*, etc.) debe verificar grupo_actor==1 del usuario autenticado, no confiar en cualquier dato del request. Every admin-only endpoint (/put_propietario, /delete_*, etc.) must verify grupo_actor==1 from the authenticated user, not trust any request data.

B. Oraculos de Enumeracion B. Enumeration Oracles

SG-6. "Olvide mi contraseña" confirma existencia de cuentas SG-6. Forgot-password confirms account existence

MedioMedium

HTTP 404 → "Correo electronico no encontrado". Exito → "Se envio a tu correo un enlace". Sin respuesta neutral. Alimenta cualquier email, obtienes si/no. No hay throttle visible del lado del cliente. HTTP 404 → "Email not found". Success → "A link was sent to your email". No neutral response. Feed any email, get yes/no. No throttle visible on the client.

File: components/CU0-Login/index.js:137-147
Solve → Responder siempre el mismo mensaje generico ("Si el correo existe, recibiras un enlace"). Agregar rate-limit por IP. Always respond with the same generic message ("If the email exists, you'll receive a link"). Add per-IP rate limiting.

SG-7. Mensaje de error revela si un email pertenece a un admin SG-7. Error message reveals if an email belongs to an admin

MedioMedium

En el flujo de transferencia, HTTP 421 devuelve: "La direccion de correo electronico introducida pertenece a un administrador". Permite confirmar que emails son cuentas de admin — objetivo para ataques de credenciales o phishing dirigido. In the transfer flow, HTTP 421 returns: "The email address belongs to an administrator". Lets an attacker confirm which emails are admin accounts — targeting for credential attacks or spear-phishing.

File: components/CU4.2-TransferirArte/index.js:447-455
Solve → Cambiar el mensaje a "El correo electronico ya esta registrado" sin distinguir rol. El contexto de "por favor, ingresa uno diferente" es suficiente para el flujo UX. Change the message to "Email already registered" without revealing role. The "please enter a different one" context is enough for the UX flow.

SG-8. Enumeracion independiente de codigo de pieza y de invitacion SG-8. Independent enumeration of piece code and invitation code

MedioMedium

Tres codigos de error distintos en /post_registro_arte: 411 = pieza no encontrada, 412 = invitacion no encontrada, 413 = ambos validos pero no pertenecen a la misma pieza. Permite adivinar codigos de pieza e invitacion independientemente. Combinado con la validacion solo-alfanumerico (sin longitud minima, sin entropia requerida), el espacio de claves es atacable por fuerza bruta. Three distinct error codes on /post_registro_arte: 411 = piece not found, 412 = invitation not found, 413 = both valid but don't belong to the same piece. Lets you brute piece codes and invitation codes independently. Combined with alphanumeric-only validation (no min length, no entropy required), the keyspace is brute-forceable.

File: components/CU4-RegistrandoArte/RegistroArte/index.js:262-288
Solve → Unificar los tres casos en un solo error generico ("codigos invalidos"). Agregar rate-limit a /post_registro_arte. Forzar codigos con longitud minima 16 y entropia suficiente en el servidor. Merge all three cases into a single generic error ("invalid codes"). Add rate-limit to /post_registro_arte. Enforce minimum-length 16 and sufficient entropy on codes server-side.

C. Confianza Indebida en el Cliente (configuracion para IDOR) C. Server-trust-client (IDOR setup)

SG-9. Lectura de propietarios via fk_propietario de estado de navegacion (IDOR potencial) SG-9. Owner-read via fk_propietario from navigation state (potential IDOR)

AltoHigh

El frontend envia id_propietario: locationData?.fk_propietario desde el estado de navegacion (manipulable en DevTools) a GET /get_propietarios. El servidor responde con el perfil completo del propietario: nombre, email, telefono, codigo de invitacion. Si el backend no verifica que el usuario autenticado tiene derecho a ver ese ID: IDOR clasico — cualquier usuario autenticado lee cualquier perfil. The frontend sends id_propietario: locationData?.fk_propietario from navigation state (tamperable in DevTools) to GET /get_propietarios. The server returns the owner's full profile: name, email, phone, invitation code. If the backend doesn't verify the authenticated user has rights to view that ID: classic IDOR — any auth'd user reads any profile.

File: components/CU4.2-TransferirArte/index.js:594-599
api.get(`/get_propietarios`, { ...config, params: { id_propietario: locationData?.fk_propietario }})
Solve → Verificacion server-side: si el usuario no es admin, solo puede pedir su propio id_propietario. Test: autenticarse como usuario no-admin, tamperar fk_propietario, confirmar que retorna 403. Server-side check: if user is not admin, can only request their own id_propietario. Test: authenticate as non-admin, tamper fk_propietario, confirm it returns 403.

SG-10. Modificacion de propietario con id_propietario de navegacion (IDOR potencial) SG-10. Owner-update with id_propietario from navigation (potential IDOR)

AltoHigh

El putPropietario envia id_propietario: locationData?.fk_propietario y todos los campos editables (nombre, apellidos, celular, email, codigo_invitacion). Dispara por SG-5 (estado de navegacion decide admin). Si el servidor no re-verifica que el usuario puede editar ese ID, un no-admin con locationData.admin=true tamperado edita cualquier propietario. putPropietario sends id_propietario: locationData?.fk_propietario and all editable fields (name, last names, mobile, email, invitation code). Triggered by SG-5 (nav state decides admin). If the server doesn't re-verify the user can edit that ID, a non-admin with tampered locationData.admin=true edits any owner.

File: components/CU4.2-TransferirArte/index.js:670-684
Solve → Mismo fix que SG-5 y SG-9: verificacion explicita de rol y de ownership del recurso en el backend. Same fix as SG-5 and SG-9: explicit role + resource-ownership check on the backend.

D. Facilitadores de Phishing y Spam D. Phishing & Mail-Relay Enablers

SG-11. El cuerpo HTML del email se compone del lado del cliente y el servidor lo re-envia SG-11. HTML email body is client-composed and server-relayed

CriticoCritical

La funcion returnStringHTML construye un <html>...</html> completo del lado del cliente — con nombres, piezas, codigo de invitacion, y logo embebido. Ese HTML va como content al POST a /put_transferir_arte, que lo reenvia via el gateway de correo. Mismo patron en hooks/sendEmail.js hacia /post_send_email. Si la autenticacion es debil o ausente en esos endpoints: plataforma de phishing lista para usar, pre-marcada como Ecstatic Crafts, con HTML totalmente controlado por el atacante. The returnStringHTML function builds a complete <html>...</html> client-side — with names, pieces, invitation code, and embedded logo. That HTML is sent as content via POST to /put_transferir_arte, which relays it via the mail gateway. Same pattern in hooks/sendEmail.js/post_send_email. If auth is weak or missing on those endpoints: turn-key phishing platform, pre-branded as Ecstatic Crafts, with HTML fully under attacker control.

Files: components/CU4.2-TransferirArte/index.js:220-392, 414, hooks/sendEmail.js
Payload ejemplo: { email_list: ["victima@gmail.com"], subject: "...", content: "<html>...phishing...</html>", content_type: "html", origin: "..." }
Es como tener un sello oficial de la empresa y papel con membrete al alcance de la mano. Con las credenciales correctas, cualquiera puede imprimir cartas que se ven 100% oficiales, firmadas por Ecstatic Crafts, y mandarlas a quien quiera. It's like having the company's official stamp and letterhead right at hand. With the right credentials, anyone can print letters that look 100% official, signed by Ecstatic Crafts, and send them to anyone.
Solve → El backend debe componer el HTML usando templates server-side, no recibirlo del cliente. Si el template necesita datos dinamicos, recibir solo los campos parametricos (nombre, codigo) y renderizarlos en un template fijo. Y: requerir autenticacion estricta en /post_send_email + allowlist de destinatarios (solo el email del usuario autenticado). The backend must compose HTML from server-side templates, not receive it from the client. If the template needs dynamic data, accept only parametric fields (name, code) and render into a fixed template. And: require strict auth on /post_send_email + recipient allowlist (only the authenticated user's email).

SG-12. El codigo de invitacion viaja como texto plano en el cuerpo del email SG-12. Invitation code travels plaintext in the email body

AltoHigh

El codigo de invitacion es la credencial que otorga al receptor el derecho de reclamar la obra. Se renderiza como texto plano en el HTML del correo (${f.codigo_invitacion}). Termina en: logs del servidor de correo, cache de buzones, reenvios de email, backups de Gmail. Cualquiera con acceso al email tiene autorizacion de transferencia. The invitation code is the credential that grants the recipient the right to claim the artwork. It's rendered plaintext in the email HTML (${f.codigo_invitacion}). Ends up in: mail server logs, mailbox caches, email forwards, Gmail backups. Anyone with email access has transfer authorization.

File: components/CU4.2-TransferirArte/index.js:369
Solve → En lugar de enviar el codigo crudo, enviar un enlace de un solo uso con TTL (ej. app.ec.../claim?token=HASH) que el servidor intercambia por el codigo real al abrirse. El enlace en logs no sirve despues del primer uso. Instead of sending the raw code, send a single-use link with TTL (e.g. app.ec.../claim?token=HASH) that the server exchanges for the real code when opened. The link in logs is useless after first use.

SG-13. El remitente elige el codigo de invitacion del proximo propietario SG-13. Sender picks the next owner's invitation code

AltoHigh

En la transferencia, codigo_invitacion y codigo_invitacion_generado reciben el MISMO valor del input del usuario — ambos se envian identicos al backend. No hay longitud minima, no hay entropia requerida, solo validationIsAlphaNumeric. Un propietario saliente malicioso puede setear codigo_invitacion = "1" — y el nuevo enlace es trivialmente adivinable. Sin TTL ni use-once visible en el cliente. In the transfer, codigo_invitacion and codigo_invitacion_generado receive the SAME value from user input — both sent identical to the backend. No min length, no entropy required, only validationIsAlphaNumeric. A malicious outgoing owner can set codigo_invitacion = "1" — and the new link is trivially guessable. No TTL or single-use semantics visible on the client.

File: components/CU4.2-TransferirArte/index.js:406-407, 190-194
codigo_invitacion: f.codigo_invitacion, codigo_invitacion_generado: f.codigo_invitacion
Solve → El codigo de invitacion nuevo debe generarse server-side con entropia adecuada (crypto.randomBytes equivalente, ≥128 bits). El cliente nunca lo elige. TTL + single-use obligatorios. The new invitation code must be generated server-side with adequate entropy (crypto.randomBytes equivalent, ≥128 bits). The client never picks it. TTL + single-use required.

E. Defaults Criptograficos / Diseño E. Cryptographic / Design Defaults

SG-14. Sin cabecera Content-Security-Policy SG-14. No Content-Security-Policy header

AltoHigh

Las respuestas del servidor incluyen X-Frame-Options, X-Content-Type-Options, Referrer-Policy y Permissions-Policy — pero CERO CSP. Combinado con tokens en localStorage (S22) y scripts de unpkg sin SRI (S23): cualquier XSS puede exfiltrar tokens a cualquier dominio sin restriccion. Server responses include X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy — but ZERO CSP. Combined with tokens in localStorage (S22) and unpkg scripts without SRI (S23): any XSS can exfiltrate tokens to any domain with no restriction.

Fuente: captura de headers en evidence/phase-2/subdomains/app.headers.txt
Solve → Agregar en Apache: Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://unpkg.com; connect-src 'self' https://api.ecstaticcrafts.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com". Modo report-only primero para calibrar. Add in Apache: Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://unpkg.com; connect-src 'self' https://api.ecstaticcrafts.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com". Report-only mode first to calibrate.

SG-15. Auto-login desde estado de navegacion SG-15. Auto-login from navigation state

MedioMedium

La ruta /consultar-artes auto-autentica al usuario si el estado de navegacion trae { email, pin, autoLogin: true }. Cualquier flujo interno que navegue con esos campos inicia sesion sin click. El riesgo depende de donde se pueblan esos campos — si viene de un enlace en un email (email + pin en URL params), entonces el enlace es una credencial transmisible. The /consultar-artes route auto-authenticates if navigation state carries { email, pin, autoLogin: true }. Any internal flow that navigates with those fields logs in without a click. Risk depends on where those fields are populated — if it comes from an email link (email + pin in URL params), the link becomes a transferable credential.

File: components/CU0-Login/index.js:38-40, 296-298
Solve → Revisar donde se popula location.state.dataCache con credenciales. Si viene de enlaces en email, migrar a tokens one-time (SG-12 fix aplica aqui tambien). Audit where location.state.dataCache gets populated with credentials. If it comes from email links, migrate to one-time tokens (SG-12 fix applies here too).

SG-16. Portal.js es un componente vacio SG-16. Portal.js is an empty component

BajoLow

La ruta /portal renderiza <Content></Content> — un div estilizado vacio. No es explotable. Indicador de superficie obsoleta / flujo a medio implementar. Senala que la base de codigo tiene areas olvidadas — donde suelen esconderse otras suposiciones rotas. The /portal route renders <Content></Content> — an empty styled div. Not exploitable. Marker of stale surface area / half-implemented flow. Signals this codebase has forgotten areas — where other broken assumptions tend to hide.

File: components/CU0-Login/Portal.js
Solve → Eliminar la ruta y el componente. Es ruido que distrae en la revision. Remove the route and component. It's noise that distracts during review.

Resumen — Revision a nivel de codigo Summary — Code-level review

16 hallazgos concretos con referencias a archivo y linea. Cuatro categorias de riesgo: autenticacion debil (SG-1 a SG-5), oraculos de enumeracion (SG-6 a SG-8), IDOR potencial (SG-9, SG-10), y spam/phishing (SG-11 a SG-13). La solucion de raiz — desmontar la app si esta abandonada — cierra los 16. Si se planea reactivar, cada uno requiere fix explicito antes de re-exponer. La buena noticia: el codigo no tiene dangerouslySetInnerHTML, eval, ni console.log de produccion. La mala: las vulnerabilidades no son bugs accidentales — son decisiones de arquitectura que confian demasiado en el cliente. 16 concrete findings with file and line references. Four risk categories: weak authentication (SG-1 through SG-5), enumeration oracles (SG-6 through SG-8), potential IDOR (SG-9, SG-10), and spam/phishing (SG-11 through SG-13). The root-cause fix — decommission the app if abandoned — closes all 16. If reactivation is planned, each one needs an explicit fix before re-exposing. Good news: the code has no dangerouslySetInnerHTML, no eval, no production console.log. Bad news: the vulnerabilities aren't accidental bugs — they're architectural decisions that over-trust the client.

S16. rpcbind (puerto 111) expuesto S16. rpcbind (port 111) exposed

BajoLow

Puerto 111 TCP+UDP corriendo rpcbind (Sun RPC portmapper). Servicios RPC no deberian estar expuestos a internet — se usan para NFS y servicios internos. Puede contribuir a ataques de amplificacion DDoS. Port 111 TCP+UDP running rpcbind (Sun RPC portmapper). RPC services should not be exposed to the internet — they're used for NFS and internal services. Can contribute to DDoS amplification attacks.

Solve → Bloquear en el firewall (iptables/firewalld) o deshabilitar el servicio si NFS no se usa (systemctl disable rpcbind). Block in the firewall (iptables/firewalld) or disable the service if NFS isn't used (systemctl disable rpcbind).

S17. DNS (puerto 53) accesible externamente S17. DNS (port 53) externally accessible

BajoLow

Puerto 53 abierto con respuesta NOTIMP. El servidor DNS es autoritativo para el dominio — esto es normal. El riesgo seria si ademas funciona como resolver recursivo abierto (vector de amplificacion DNS), pero nmap devolvio NOTIMP, lo que sugiere que no lo es. Port 53 open with NOTIMP response. The DNS server is authoritative for the domain — this is normal. The risk would be if it also functions as an open recursive resolver (DNS amplification vector), but nmap returned NOTIMP, suggesting it doesn't.

wpscan: WordPress-especifico wpscan: WordPress-specific

WordPress 6.9.4 (ultimo), tema Goya 1.0.9.6 (ultimo). Enumeracion de plugins bloqueada por directory listing deshabilitado (bien — resultado de Fase 2). Un usuario enumerado: web2021 (via patron de autor). Sin backups de configuracion encontrados. WordPress 6.9.4 (latest), Goya theme 1.0.9.6 (latest). Plugin enumeration blocked by disabled directory listing (good — result of Phase 2). One user enumerated: web2021 (via author pattern). No configuration backups found.

TLS: resuelto completamente TLS: fully resolved

La prueba de TLS en Fase 1 mostro TLS 1.1 aun habilitado (parcial). Fase 3 confirma: tanto TLS 1.0 como TLS 1.1 estan ahora bloqueados. Solo TLSv1.2 y TLSv1.3 aceptados. Todos los cipher suites grado A. Sin Heartbleed (verificado por sslscan). El hallazgo S1 esta ahora completamente resuelto. The TLS test in Phase 1 showed TLS 1.1 still enabled (partial). Phase 3 confirms: both TLS 1.0 and TLS 1.1 are now blocked. Only TLSv1.2 and TLSv1.3 accepted. All cipher suites grade A. No Heartbleed (verified by sslscan). Finding S1 is now fully resolved.

Acciones por prioridad Actions by priority

  1. Apagar Django en puerto 8000 Shut down Django on port 8000
    Resuelve S11 + S19 de un solo golpe. Si la API no se usa, apagar gunicorn y bloquear el puerto. Solves S11 + S19 in one move. If the API isn't in use, stop gunicorn and block the port.
    Impacto: criticoImpact: criticalEsfuerzo: 5 minEffort: 5 min
  2. Cerrar MySQL al exterior Close MySQL to the outside
    bind-address = 127.0.0.1 en my.cnf. Reiniciar MySQL. bind-address = 127.0.0.1 in my.cnf. Restart MySQL.
    Impacto: altoImpact: highEsfuerzo: 5 minEffort: 5 min
  3. Firewall: cerrar puertos innecesarios Firewall: close unnecessary ports
    Bloquear 111 (rpcbind), 8000 (Django). Evaluar si 110/143 (POP3/IMAP sin cifrar) son necesarios — 993/995 son las versiones cifradas. Block 111 (rpcbind), 8000 (Django). Evaluate whether 110/143 (unencrypted POP3/IMAP) are needed — 993/995 are the encrypted versions.
    Impacto: medioImpact: mediumEsfuerzo: 15 minEffort: 15 min
  4. Eliminar app muerta y subdominio api Remove dead app and api subdomain
    Eliminar el vhost de app. y api. si la app React/Django no se usa. Reducir superficie de ataque. Remove the app. and api. vhosts if the React/Django app isn't in use. Reduce attack surface.
    Impacto: medioImpact: mediumEsfuerzo: 10 minEffort: 10 min
  5. Activar Cloudflare Activate Cloudflare
    Resuelve WAF, rate limiting, DDoS protection, encabezados de seguridad, HTTP/2, y mejora adicional de rendimiento. Un solo cambio con multiples beneficios. Solves WAF, rate limiting, DDoS protection, security headers, HTTP/2, and additional performance improvement. One change with multiple benefits.
    Impacto: altoImpact: highEsfuerzo: 30 minEffort: 30 min

Herramientas utilizadas Tools used

HerramientaTool PropositoPurpose
nmapEscaneo de puertos top 1000, deteccion de servicios, enumeracion TLSTop 1000 port scan, service detection, TLS enumeration
subfinderEnumeracion de subdominiosSubdomain enumeration
nucleiDeteccion de vulnerabilidades conocidasKnown vulnerability detection
wpscanEscaneo especifico de WordPressWordPress-specific scanning
wafw00fDeteccion de WAFWAF detection
sslscanVerificacion de configuracion TLSTLS configuration verification
testssl.shAuditoria TLS/SSL exhaustivaComprehensive TLS/SSL audit

Metodologia Methodology

Fase 1 (2026-04-09): Todas las mediciones se tomaron desde Moon (Pop!_OS, region Panama). Rendimiento: curl para temporalizacion y encabezados, parseo raw del HTML para conteo de activos y deteccion de plugins, tres ejecuciones consecutivas para validar la consistencia del TTFB. Seguridad: Reconocimiento externo unicamente — analisis de encabezados HTTP, enumeracion de directorios, sondeo de archivos, pruebas de protocolo TLS (OpenSSL), descubrimiento de rutas de la REST API, enumeracion de archivos de autor, fingerprinting de versiones. Sin pruebas autenticadas, sin explotacion, sin ataques de credenciales, sin fuzzing.

Fase 2 (2026-04-10): Sesion de optimizacion con acceso administrativo a WordPress. Se configuro WP Rocket (cache, JS diferido, LazyLoad, heartbeat, limpieza de base de datos, precarga), se eliminaron ~36 plugins (~24 inactivos + 12 activos innecesarios), se aplicaron reglas .htaccess via AIOS (Options -Indexes, bloqueo de install.php, rewrite de author enumeration), se eliminaron archivos por defecto de WP, se deshabilito la edicion de PHP, y se activo la proteccion contra hotlinking. Se instalo Asset CleanUp (limpieza de HTML: emoji scripts, generator meta, oEmbed, RSD, wlwmanifest, REST API links; XML-RPC deshabilitado). Mediciones post-optimizacion confirmadas con multiples ejecuciones consecutivas de curl. TTFB final: ~1.2s (mejora del 69%).

Fase 3 (2026-04-11/12): Pentest de infraestructura desde Moon. Herramientas: nmap (top 1000, deteccion de servicios, enumeracion de cipher suites), subfinder (subdominios), nuclei (vulnerabilidades conocidas), wpscan (WordPress-especifico), wafw00f (deteccion de WAF), sslscan + testssl.sh (auditoria TLS). Velocidad limitada a 2-5 req/s. Sin explotacion, sin ataques de credenciales. Se descubrio un segundo stack de aplicaciones (Django/React) en los puertos 8000 y subdominio app — no formaba parte del alcance original pero representaba el mayor riesgo encontrado.
Phase 1 (2026-04-09): All measurements taken from Moon (Pop!_OS, Panama region). Performance: curl for timing and headers, raw HTML parsing for asset counts and plugin detection, three consecutive runs to validate TTFB consistency. Security: External-only reconnaissance — HTTP header analysis, directory enumeration, file probing, TLS protocol testing (OpenSSL), REST API route discovery, author archive enumeration, version fingerprinting. No authenticated testing, no exploitation, no credential attacks, no fuzzing.

Phase 2 (2026-04-10): Optimization session with WordPress admin access. Configured WP Rocket (cache, deferred JS, LazyLoad, heartbeat, database cleanup, preload), removed ~36 plugins (~24 inactive + 12 unnecessary active), applied .htaccess rules via AIOS (Options -Indexes, install.php block, author enumeration rewrite), deleted WP default files, disabled PHP editing, and enabled hotlink protection. Installed Asset CleanUp (HTML cleanup: emoji scripts, generator meta, oEmbed, RSD, wlwmanifest, REST API links; XML-RPC disabled). Post-optimization measurements confirmed with multiple consecutive curl runs. Final TTFB: ~1.2s (69% improvement).

Phase 3 (2026-04-11/12): Infrastructure pentest from Moon. Tools: nmap (top 1000, service detection, cipher suite enumeration), subfinder (subdomains), nuclei (known vulnerabilities), wpscan (WordPress-specific), wafw00f (WAF detection), sslscan + testssl.sh (TLS audit). Throttled at 2-5 req/s. No exploitation, no credential attacks. Discovered a second application stack (Django/React) on port 8000 and app subdomain — not part of the original scope but represented the highest risk found.