Contents
Introducción
Los archivos SVG (Scalable Vector Graphics) ofrecen enormes ventajas: son vectoriales, escalables sin pérdida y suelen pesar poco. Sin embargo, un SVG no es solo una imagen: puede contener código XML/HTML, JavaScript embebido, referencias externas y atributos que ejecutan comportamientos en el navegador. Por eso, permitir la subida de SVG en WordPress sin medidas de seguridad puede abrir brechas importantes (XSS, fugas de información, ejecución remota).
Objetivo del tutorial
Explicar de forma detallada y práctica cómo permitir la subida de SVG en WordPress de forma segura, aplicando sanitización al momento de la subida (y proporcionando herramientas para sanitizar archivos ya subidos), usando una librería especializada recomendada y mostrando ejemplos listos para implementar como plugin.
Riesgos principales al aceptar SVG sin filtrar
- Cross-Site Scripting (XSS): etiquetas ltscriptgt, eventos inline (onload, onclick), URIs javascript:.
- Inyección de HTML: uso de ltforeignObjectgt para incrustar HTML con scripts.
- Referencias externas: imágenes, fuentes o scripts que apunten a dominios externos (fugas de información o CSRF).
- Animaciones o intencionalidades ocultas: SMIL/JS que ejecuten acciones no deseadas.
- Mime-sniffing / ejecución como HTML: servidores mal configurados pueden servir SVG como HTML y permitir ejecución.
Estrategia recomendada
- Permitir la extensión/mime SVG en WordPress con filtros controlados.
- Restringir qué usuarios pueden subir SVG (por ejemplo, administradores o roles concretos).
- Sanitizar el contenido del SVG en el proceso de subida usando una librería probada (no un simple regex).
- Reescribir el archivo temporal con la versión sanitizada antes de que WordPress lo mueva a /uploads.
- Opcional: sanitizar al mostrar (doble verificación) o servir SVGes desde un endpoint que los vuelva a filtrar.
- Mantener la librería de sanitización actualizada y auditar periódicamente.
Qué librería usar
En PHP hay librerías específicas para sanitizar SVG. La más utilizada y madura es enshrined/svg-sanitize. Hace un parsing correcto del XML y elimina elementos/atributos peligrosos según configuración. Es preferible a soluciones caseras porque entiende la estructura del SVG.
Instalación (composer):
composer require enshrined/svg-sanitize
Implementación paso a paso: plugin ejemplo
El ejemplo que sigue muestra un plugin mínimo que:
- Permite MIME types SVG
- Corrige la comprobación interna de WordPress para la extensión
- Sanitiza el contenido al subir
- Restringe la subida a usuarios con la capacidad configurada
Coloca este archivo como plugin (por ejemplo: wp-content/plugins/svg-sanitizer/svg-sanitizer.php) y crea el directorio vendor con composer autoload que incluye enshrined/svg-sanitize.
lt?php / Plugin Name: SVG Upload Sanitizer Description: Permite subir SVGs y los sanitiza con enshrined/svg-sanitize. Restrinja la subida por capacidad. Version: 1.0 Author: Ejemplo / if ( ! defined( ABSPATH ) ) { exit } // Si usas Composer, cargamos autoload if ( file_exists( __DIR__ . /vendor/autoload.php ) ) { require_once __DIR__ . /vendor/autoload.php } class WP_SVG_Upload_Sanitizer { // Capacidad mínima para permitir subir SVG. Cambia según necesidad. private capability = manage_options public function __construct() { add_filter( upload_mimes, array( this, allow_svg_mime ) ) add_filter( wp_check_filetype_and_ext, array( this, fix_svg_filetype_check ), 10, 4 ) add_filter( wp_handle_upload_prefilter, array( this, sanitize_svg_on_upload ) ) } // Añade resoluciones MIME public function allow_svg_mime( mimes ) { mimes[svg] = image/svg xml mimes[svgz] = image/svg xml return mimes } // Evitar que WP rechace el archivo por extensión/tipo public function fix_svg_filetype_check( data, file, filename, mimes ) { ext = pathinfo( filename, PATHINFO_EXTENSION ) if ( strtolower( ext ) === svg strtolower( ext ) === svgz ) { data[ext] = svg data[type] = image/svg xml } return data } // Sanitize cuando se sube public function sanitize_svg_on_upload( file ) { if ( empty( file[name] ) ) { return file } ext = strtolower( pathinfo( file[name], PATHINFO_EXTENSION ) ) if ( ext !== svg ext !== svgz ) { return file // no es SVG } // Restricción por capacidad: solo permitir a determinados usuarios if ( ! current_user_can( this->capability ) ) { file[error] = No tienes permiso para subir archivos SVG. return file } tmp_name = file[tmp_name] if ( ! is_readable( tmp_name ) ) { file[error] = Error leyendo el archivo SVG. return file } contents = file_get_contents( tmp_name ) if ( contents === false ) { file[error] = No se pudo leer el SVG. return file } // En caso de svgz (gzip) lo descomprimimos antes de sanitizar if ( ext === svgz ) { decoded = @gzdecode( contents ) if ( decoded === false ) { file[error] = SVGZ inválido o comprimido incorrectamente. return file } contents = decoded } // Comprobación básica: contiene
Notas sobre el plugin de ejemplo
- Cambia la propiedad capability si quieres permitir la subida a otros roles (por ejemplo upload_files o una capacidad personalizada).
- El plugin usa composer autoload asegúrate de que vendor/autoload.php esté presente o adapta la carga de la librería manualmente.
- El ejemplo maneja svgz (gzip) descomprimiendo y recomprimiendo puedes eliminar esa parte si no necesitas soporte svgz.
Sanitizar SVGs ya existentes
Si ya tienes SVGs subidos y quieres sanitizarlos en masa o de uno en uno, puedes usar funciones administrativas que reescriban los archivos en uploads tras sanitizarlos.
// Sanitiza un attachment SVG por ID (usa desde un script administrativo) function sanitize_attachment_svg_by_id( attachment_id ) { if ( ! function_exists( get_attached_file ) ) { return new WP_Error( missing_function, Función get_attached_file no disponible ) } path = get_attached_file( attachment_id ) if ( ! path ! file_exists( path ) ) { return new WP_Error( no_file, Archivo no encontrado ) } ext = strtolower( pathinfo( path, PATHINFO_EXTENSION ) ) if ( ext !== svg ext !== svgz ) { return new WP_Error( not_svg, No es un SVG. ) } contents = file_get_contents( path ) if ( contents === false ) { return new WP_Error( read_failed, No se pudo leer. ) } if ( ext === svgz ) { decoded = @gzdecode( contents ) if ( decoded === false ) { return new WP_Error( invalid_svgz, SVGZ inválido. ) } contents = decoded } sanitizer = new enshrinedsvgSanitizerSanitizer() clean = sanitizer->sanitize( contents ) if ( clean === false ) { return new WP_Error( sanitize_failed, No se pudo sanitizar. ) } to_write = ( ext === svgz ) ? gzencode( clean ) : clean if ( file_put_contents( path, to_write ) === false ) { return new WP_Error( write_failed, Error al escribir el fichero. ) } return true }
Medidas complementarias de seguridad
- Headers HTTP: asegúrate de servir SVG con Content-Type: image/svg xml y añade X-Content-Type-Options: nosniff para evitar sniffing.
- Política CSP (Content-Security-Policy): establece un CSP restrictivo para bloquear la ejecución de scripts desde orígenes no permitidos.
- Evitar inline SVG no sanitizado: si insertas SVG inline en HTML (por ejemplo, mediante editor), vuelve a sanitizar antes de echo en plantilla.
- Backups: haz copias antes de reescribir archivos en masa.
- Reglas de servidor: considera servir SVG desde un subdominio separado o configurar cabeceras y políticas de seguridad específicas para /wp-content/uploads/.
- Auditoría y actualizaciones: mantén la librería de sanitización actualizada y revisa logs de carga para detectar intentos maliciosos.
Tests y validación
- Prueba subir SVGs benignos (iconos, logos) y SVGs maliciosos (con ltscriptgt, onload, javascript: etc.) para comprobar rechazo/limpieza.
- Inspecciona el archivo final en /uploads para confirmar que los elementos/atributos peligrosos se han eliminado.
- Usa herramientas automáticas o linters SVG para verificar validez XML tras sanitizar.
Consideraciones finales
Permitir SVG en WordPress es posible y muy útil, pero exige un enfoque conservador: solo usuarios de confianza deberían poder subirlos, siempre hay que aplicar sanitización con una librería especializada y revisar cabeceras/servicio del servidor. El enfoque de sanitizar al subir minimiza el riesgo, y añadir una sanitización adicional al servir o al insertar inline ofrece una segunda línea de defensa.
Recursos
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |