En nuestro análisis forense de un ataque tipo Magecart planteamos una idea que, en retrospectiva, merecía su propio artículo: limpiar el malware de una tienda no es lo mismo que arreglar la tienda. Quitamos el skimmer, las web shells y los binarios SUID-root de aquella tienda PrestaShop 1.7.x — y poco después volvió a ser atacada. No por una puerta trasera que se nos escapó, sino por la puerta principal: una vulnerabilidad a nivel de aplicación que la limpieza había contenido pero nunca cerrado. Esta entrada es el manual que habríamos querido aplicar la primera vez — cómo endurecer una tienda PrestaShop que todavía no se puede actualizar, de modo que quitar la carga útil cierre también el agujero por el que entró.

Si hoy puedes pasar a PrestaShop 8 o 9, hazlo — es la única solución duradera, y el resto de este artículo es un paliativo. Pero para una tienda no trivial, una actualización es un proyecto de varias semanas (compatibilidad de módulos, refactorización del tema, recableado de pagos). El interludio es real, y durante él eres un objetivo. Así se sobrevive a él. Este es el complemento avanzado de nuestra lista de comprobación de endurecimiento de PrestaShop; empieza allí por lo básico y vuelve aquí por la capa de «incidente».

1. Encuentra el vector de entrada, no solo la carga útil

El error más caro en respuesta a incidentes es confundir el síntoma visible con la herida. Un skimmer en el JavaScript del tema es el síntoma. La herida es lo que permitió a un atacante escribir ese JavaScript — una web shell, un módulo sin parchear, un punto de inyección. Quita el skimmer dejando la herida abierta y habrás comprado días, no seguridad.

Antes de endurecer nada, responde con pruebas a una pregunta: ¿cómo llegó código al disco? Lee los registros de acceso alrededor de las marcas de tiempo de creación de los archivos maliciosos (usa ctime, no mtime — los atacantes falsifican mtime). Correlaciona patrones POST sospechosos, parámetros de consulta inusuales y anomalías en el tamaño de respuesta con el momento en que apareció la carga útil. Si no puedes nombrar el vector de entrada, asume que sigue abierto.

2. Aplica «parches virtuales» a la cadena de ataque conocida de 1.7.x

PrestaShop 1.7.x tiene un puñado de vulnerabilidades bien conocidas y armadas que los escáneres automáticos encadenan. Si no puedes actualizar, parchea directamente las rutas de código concretas. Son paliativos — sigue los avisos oficiales — pero cierran las puertas que se están forzando activamente.

2.1 Inyección SQL en la búsqueda por facetas

Las versiones antiguas de ps_facetedsearch concatenan los valores de filtro directamente en el SQL. Es una primitiva de inyección SQL a ciegas, y en una tienda 1.7.x suele ser el verdadero punto de entrada. Escapa y castea cada valor procedente de la petición:

// BEFORE (vulnerable): raw value concatenated into the WHERE clause
$conditions[] = $alias.'.'.$field.$operator.current($values);

// AFTER: string values escaped, numeric ranges cast
$conditions[] = $alias.'.'.$field.$operator."'".pSQL(current($values))."'";
$orConditions[] = $alias.'.'.$field.$operator.(float) $value;

2.2 El RCE de la caché MySQL de Smarty (CVE-2022-36408)

Es la mitad de la cadena que convierte el acceso a la base de datos en ejecución de código: la caché de plantillas Smarty respaldada por base de datos puede ser forzada a renderizar PHP controlado por el atacante. La mayoría de las tiendas nunca la usan. Desactívala por completo en config/smarty.config.inc.php para que sea inalcanzable:

// AFTER: the MySQL cache include is permanently switched off
if (false /* CVE-2022-36408 mitigation: MySQL Smarty cache disabled */) {
    include _PS_CLASS_DIR_.'Smarty/SmartyCacheResourceMysql.php';
    $smarty->caching_type = 'mysql';
}

Después confirma que la tienda usa caché en sistema de archivos (PS_SMARTY_CACHING_TYPE = filesystem). Una inyección SQL por sí sola es grave; una inyección SQL capaz de escribir una plantilla PHP en una caché y ejecutarla es una toma de control total.

2.3 Módulos abandonados (RevSlider y similares)

Los módulos viejos y sin mantenimiento son un vector recurrente. Si no puedes eliminar un módulo porque el tema depende de él, prohíbe la ejecución web directa de sus scripts. El renderizado del front-end ocurre mediante hooks de PrestaShop del lado del servidor, no llamando directamente a los archivos PHP del módulo:

# modules/<module>/.htaccess
<FilesMatch "\.php$">
    Require all denied
</FilesMatch>

Y bloquea los archivos de entrada estáticos que el atacante vuelve a apuntar tras cada limpieza. En sistemas de archivos que honran los atributos extendidos (ext4/xfs/btrfs): chattr +i index.php config/*.php. Una futura shell no podrá sobrescribir un archivo inmutable sin ejecutar antes chattr -i, que requiere root.

3. Bloquea el runtime de PHP — correctamente

Desactivar las funciones que una shell usa para lanzar procesos del sistema operativo merece la pena, pero es el paso que más a menudo se hace mal — de una forma que parece aplicada pero no lo está.

disable_functions = exec,system,shell_exec,passthru,popen,proc_open,
                    proc_close,proc_get_status,proc_terminate,dl,show_source,phpinfo

Tres cosas que se pasan por alto:

  • disable_functions es de tipo PHP_INI_SYSTEM. Solo se aplica desde el php.ini de arranque del SAPI que sirve realmente el sitio. En una máquina gestionada por panel, el SAPI web suele ser un pool PHP-FPM concreto o una instancia de Apache para esa versión — no el PHP CLI del sistema. Edita el php.ini correcto y reinicia exactamente ese servicio, o tu cambio no hará nada.
  • ini_get('disable_functions') miente. Puede informar de una lista de bloqueo completa que no se aplica realmente. La única prueba fiable es una real, ejecutada vía web: un script temporal (nombre aleatorio, eliminado de inmediato) que llame a function_exists('exec') e intente @exec('id'), recuperado por HTTP. Fíate del resultado del intento, no de la configuración informada.
  • No es un interruptor de emergencia. Impide que una shell PHP lance comandos del sistema. No neutraliza una shell PHP basada en eval — que aún puede leer y escribir archivos dentro de open_basedir y dialogar con la base de datos usando las credenciales de parameters.php. Trátalo como una capa, no como la respuesta.

El núcleo de PrestaShop usa exec() en un par de rutas de imagen/MIME que tienen reservas con finfo, así que desactivarlo no rompe las subidas — aun así, prueba las subidas de imágenes en staging antes de aplicarlo en producción.

4. Pon Cloudflare delante — como capa de parche virtual

Un WAF en proxy inverso es lo más parecido a un parche virtual desplegable en minutos. Pero solo funciona si los atacantes no pueden esquivarlo.

4.1 Bloquea el origen a Cloudflare

Un WAF delante de un origen que aún responde en su IP pública es opcional desde el punto de vista del atacante. Restringe los puertos web del servidor para que solo Cloudflare pueda alcanzarlos:

# allow 80/443 only from Cloudflare's published ranges, drop the rest
iptables -N CF_WEB
iptables -A CF_WEB -s 173.245.48.0/20 -j ACCEPT   # × Cloudflare's IPv4 ranges
#                                                  (+ the IPv6 ranges, + 127.0.0.1)
iptables -A CF_WEB -j DROP
iptables -I INPUT -p tcp -m multiport --dports 80,443 -j CF_WEB

El truco que la mayoría olvida: tu DNS de correo filtra la IP del origen. Un registro MX, un registro SPF o un subdominio mail. sin proxy entrega la dirección real al atacante — y es el firewall de arriba lo que hace inocua esa fuga para la web. Pasa por proxy de Cloudflare cada registro web y protege el origen con firewall de todos modos. Deja SSH, correo y los puertos del panel con sus propias reglas de acceso — el bloqueo web nunca debe tocarlos.

4.2 Reglas WAF personalizadas — y la trampa de las extensiones

En el plan Free de Cloudflare tienes cinco reglas personalizadas y nada de expresiones regulares (solo contains, eq, starts_with, ends_with, lower()). Es suficiente para un conjunto de reglas enfocado:

  • Bloquear web shells y subidas ejecutables: nombres de shell conocidos, llamadas directas al PHP de módulos abandonados y ejecución de PHP bajo los directorios de assets (/img/, /upload/, /themes/, /js/, /cache/).
  • Bloquear cargas de inyección: ../, /etc/passwd, union select, information_schema, <?php, base64_decode y las variantes codificadas.
  • Bloquear archivos sensibles: /.git/, /.env, composer.json, tus archivos de configuración.
  • Bloquear los peores crawlers molestos por user-agent.

La trampa: Apache a menudo ejecuta más que .php. Una línea AddType típica mapea .php .php3 .php4 .phtml al handler de PHP. Una regla que bloquea .phtml/.phar/.php5 pero olvida .php3 y .php4 deja abierta una puerta para subir una shell — encontramos exactamente este fallo en nuestro propio conjunto de reglas durante una revisión. Bloquea cada extensión que el servidor vaya a ejecutar realmente y comprueba tu configuración real de AddType/AddHandler en lugar de suponer.

4.3 Limitación de tasa: conoce lo que tu plan puede y no puede hacer

La limitación de tasa es la herramienta adecuada contra las avalanchas de crawlers que martillean la búsqueda por facetas (tratamos ese ángulo DoS por separado). Pero sé preciso con tu plan. En el plan Free de Cloudflare, la única regla de limitación solo puede coincidir con la ruta de la URL — no con la cadena de consulta, ni el user-agent, ni la IP de origen — su acción se limita a block y la ventana es fija. Eso significa que en Free no puedes construir una regla «limita los bots en las URL de facetas ?q=»; la cadena de consulta le es invisible. Lo máximo es un límite anti-avalancha por IP en rutas no estáticas. Una verdadera limitación consciente de la consulta y el user-agent (con un reto gestionado en lugar de un bloqueo duro) requiere el Advanced Rate Limiting del plan Pro. Decide si la exposición de la búsqueda por facetas justifica la actualización — para una tienda atacada activamente, a menudo sí.

5. Añade detección — para cazar la segunda ronda en horas

El endurecimiento reduce las probabilidades; no te hace inmune. La diferencia entre una mala tarde y un incidente de notificación obligatoria es la rapidez con que te das cuenta. La tienda de nuestro caso de estudio estuvo skimmeando en silencio casi dos días antes de que alguien mirara. Mantenla vigilada:

  • Monitorización de integridad de archivos (AIDE, Wazuh, OSSEC o un simple cron con manifiesto de hashes) sobre el webroot, con alerta ante cualquier PHP nuevo o modificado fuera de un despliegue.
  • Barridos de IOC programados para los patrones de ataques reales — ve los indicadores en nuestra entrada de anatomía — más cualquier archivo nuevo con extensión ejecutable que aparezca en directorios de assets o subidas.
  • Revisión de los Security Events de Cloudflare según calendario, no solo tras un incidente. Tu WAF también es un sensor.

6. La cura sigue siendo la migración

Todo lo anterior es defensa en profundidad sobre software que ya no recibe actualizaciones de seguridad. Eleva el coste del ataque, cierra las puertas que se explotan activamente y te compra las semanas o meses que requiere una migración real — pero compra tiempo, no seguridad. La tienda de nuestro caso de estudio es justo la prueba: se limpió, y el mismo operador volvió a entrar por un fallo de aplicación que la limpieza no había parcheado. Planifica el paso a PrestaShop 8 o 9, en infraestructura limpia, con rotación de todas las credenciales, como la verdadera remediación. Considera el endurecimiento aquí descrito como lo que mantiene las luces encendidas hasta entonces.

Para las piezas relacionadas: la lista de endurecimiento para lo básico, el endurecimiento de admin/2FA y las reglas .htaccess para los puntos de entrada, y nuestra guía de respuesta a brechas para cuando lo peor ya ha ocurrido. Y la anatomía del ataque que motivó cada paso anterior.

Compartir esta publicación:
David Miller

David Miller

Más de una década de experiencia práctica con PrestaShop. David desarrolla módulos de comercio electrónico de alto rendimiento centrados en SEO, optimización del checkout y gestión de tiendas. Apasionado por el código limpio y los resultados medibles.

¿Te gustó este artículo?

Recibe nuestros últimos consejos, guías y actualizaciones de módulos en tu bandeja de entrada.

Comentarios

Aún no hay comentarios. ¡Sé el primero!

Sé el primero en hacer una pregunta o compartir una opinión útil.

Cargando...
Volver arriba