Contents
Introducción
Este tutorial explica paso a paso cómo proteger archivos subidos (uploads) privados en WordPress usando URLs firmadas (signatures) y reglas de rewrite / manejo por PHP. El objetivo es evitar el acceso directo a ficheros sensibles y permitir su entrega sólo cuando se valida una firma y condiciones adicionales (caducidad, usuario, IP, etc.). Se cubren alternativas para servir los ficheros (X-Sendfile, X-Accel-Redirect, PHP puro), cómo crear y verificar firmas seguras, cómo ocultar la carpeta de uploads al público y cómo integrar todo en WordPress mediante rewrite rules y hooks.
Arquitectura y consideraciones
- Almacenar archivos privados: colócalos en una subcarpeta de uploads, por ejemplo wp-content/uploads/private, o mejor fuera del webroot si puedes. Si están en uploads, evita accesos directos con reglas del servidor.
- Generación de URLs firmadas: crea una URL que incluya ruta, expiración y una firma HMAC con una clave secreta. La firma garantiza integridad y autenticidad.
- Validación: un punto central (index.php / endpoint WP) valida la firma y condiciones y luego delega la entrega al servidor o a PHP.
- Entrega eficiente: si el servidor lo soporta, usa X-Sendfile (Apache) o X-Accel-Redirect (Nginx) para que sea el propio servidor quien envie el archivo tras validación reduce carga PHP y mejora rendimiento.
- Seguridad adicional: limitar expiración, enlazar URL a usuario o IP, proteger la clave secreta (definirla en wp-config.php) y normalizar rutas para evitar path traversal.
Requisitos previos
- Acceso a los archivos de WordPress (para colocar ficheros o crear la carpeta private).
- Capacidad para añadir código PHP en un plugin o functions.php (recomendado: plugin propio para gestión).
- Permisos de configuración del servidor si quieres usar X-Sendfile o X-Accel-Redirect.
Paso 1 — Preparar carpeta segura
Crear una carpeta para archivos privados. Recomendaciones:
- Preferible fuera del webroot: /var/www/private_uploads
- Si usas wp-content/uploads/private, bloquea accesos directos mediante .htaccess (Apache) o reglas nginx.
Ejemplo .htaccess para bloquear acceso público (colocar dentro wp-content/uploads/private):
# Para Apache 2.2 y 2.4 (combinado) ltIfModule mod_authz_core.cgt Require all denied lt/IfModulegt ltIfModule !mod_authz_core.cgt Order allow,deny Deny from all lt/IfModulegt
Si prefieres nginx, ubica la carpeta fuera de la ruta pública o añade una location que deniegue el acceso directo.
Paso 2 — Guardar una clave secreta
Define una clave secreta robusta en wp-config.php (o un sistema KMS en producción). No la publiques ni la controles en repositorios sin protección.
// wp-config.php define(PRIVATE_UPLOADS_SECRET, pon-aqui-una-clave-muy-larga-y-secreta-ygenerada-aleatoriamente)
Paso 3 — Generar la URL firmada (función PHP)
La función genera una URL con parametros: ruta relativa (a la carpeta privada), timestamp de expiración y la firma HMAC. Aquí hay una función de ejemplo que recibe el ID de attachment o la ruta relativa.
/ Genera URL firmada para un archivo privado. @param string relative_path Ruta relativa dentro de la carpeta privada, por ejemplo invoices/2025/archivo.pdf @param int ttl Tiempo en segundos antes de expirar (por ejemplo 300 = 5 minutos) @param intnull user_id Si se desea atar a un usuario, pasar su ID @return string URL pública firmada / function get_protected_upload_url( relative_path, ttl = 300, user_id = null ) { secret = defined(PRIVATE_UPLOADS_SECRET) ? PRIVATE_UPLOADS_SECRET : if ( empty(secret) ) { return // no hay clave, no generar } expires = time() (int) ttl data = relative_path . . expires if ( user_id ) { data .= . (int) user_id } signature = hash_hmac(sha256, data, secret) // URL amigable: /protected// / / parts = array(protected, signature, expires) if ( user_id ) { parts[] = (int) user_id } // url-encode para la ruta parts[] = rawurlencode( relative_path ) // Usar site_url() para generar base return rtrim(site_url(), /) . / . implode(/, parts) }
Paso 4 — Rewrite rules en WordPress
Registrar una regla de rewrite y variables de query para capturar signature / expires / user / path. Añade esto en un plugin o functions.php (mejor plugin).
// 1) Añadir query vars
add_filter(query_vars, function(vars){
vars[] = protected_sig
vars[] = protected_exp
vars[] = protected_uid
vars[] = protected_path
return vars
})// 2) Registrar la regla de rewrite
add_action(init, function(){
add_rewrite_rule(
^protected/([0-9a-fA-F]{64})/([0-9] )/?([0-9] )?/(. )
|
Acepto donaciones de BAT's mediante el navegador Brave :) |