Proteger acceso directo a PDF y ZIP sin plugin en WordPress
Trabajando en un sitio de soporte de WordPress con contenido solo para usuarios registrados, incluyendo archivos PDF y ZIP subidos.
Busco una forma de prevenir el acceso directo a esos archivos PDF y ZIP dentro del directorio wp-content/uploads sin usar plugins.
Revisando preguntas antiguas, esto está MUY cerca (pero los comentarios están cerrados): https://wordpress.stackexchange.com/a/37743/18608 ya que detecta acceso directo al archivo, luego hace algunos chequeos para guardar el archivo solicitado y verificar si el usuario está logueado. Si no lo está, redirige al login de WordPress. Si está logueado, carga el archivo.
Este es el htaccess original:
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]
Sin embargo, el script bloquea el acceso a todos los archivos dentro del directorio wp-content/uploads, incluyendo imágenes - así que a menos que el usuario esté logueado, incluso imágenes de posts (que no necesitan protección) se ocultan en el front-end.
Estoy intentando revisar la RewriteRule para verificar solo archivos PDF o ZIP, para que dl-file.php solo se llame si se solicita un PDF o ZIP.
He probado lo siguiente, pero devuelve "404-File not found." al acceder a un PDF o ZIP cuando el usuario está logueado, así que aunque la verificación de tipo de archivo parece funcionar, el chequeo de dl-file.php falla.
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/([^/]*\.(pdf|zip))$ filecheck.php?file=$1 [QSA,L]
¿Cómo podría modificar esto para que solo llame a dl-file.php si se solicita un archivo pdf o zip, pero aún pasar la información correcta a dl-file.php?
Gracias, Jonathon
/*
* dl-file.php
*
* Protege archivos subidos con autenticación.
*
* @link http://wordpress.stackexchange.com/questions/37144/protect-wordpress-uploads-if-user-is-not-logged-in
*
* @author hakre <http://hakre.wordpress.com/>
* @license GPL-3.0+
* @registry SPDX
*/
require_once('wp-load.php');
is_user_logged_in() || auth_redirect();
list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);
$file = rtrim($basedir,'/').'/'.str_replace('..', '', isset($_GET[ 'file' ])?$_GET[ 'file' ]:'');
if (!$basedir || !is_file($file)) {
status_header(404);
die('404 — Archivo no encontrado.');
}
$mime = wp_check_filetype($file);
if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
$mime[ 'type' ] = mime_content_type( $file );
if( $mime[ 'type' ] )
$mimetype = $mime[ 'type' ];
else
$mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 );
header( 'Content-Type: ' . $mimetype ); // siempre enviar esto
if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) )
header( 'Content-Length: ' . filesize( $file ) );
$last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) );
$etag = '"' . md5( $last_modified ) . '"';
header( "Last-Modified: $last_modified GMT" );
header( 'ETag: ' . $etag );
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' );
// Soporte para GET condicional
$client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false;
if( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )
$_SERVER['HTTP_IF_MODIFIED_SINCE'] = false;
$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
// Si está vacío, retorna 0. Si no, intenta parsear a timestamp
$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;
// Crea un timestamp para nuestra modificación más reciente...
$modified_timestamp = strtotime($last_modified);
if ( ( $client_last_modified && $client_etag )
? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) )
: ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) )
) {
status_header( 304 );
exit;
}
// Si llegamos hasta aquí, simplemente sirve el archivo
readfile( $file );
RewriteCond %{REQUEST_FILENAME} -s RewriteRule ^wp-content/uploads/([^/]*\.(pdf|zip))$ filecheck.php?file=$1 [QSA,L]
Esto en realidad se ve bien, excepto si tienes subdirectorios adicionales dentro del directorio /uploads
. Una alternativa es incluir una condición adicional en la regla original que solo reescriba la solicitud si termina en .pdf
o .zip
. Por ejemplo:
RewriteCond %{REQUEST_URI} \.(pdf|zip)$ [NC]
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]
Realmente no debería importar, pero asegúrate de que esto vaya antes del front-controller de WordPress.

Gracias - esto tiene sentido. Estoy probando aquí, pero por alguna razón no está detectando la condición de inicio de sesión, así que ¿el PDF se está cargando cuando no hay sesión iniciada?

Gracias, MrWhite - este era el camino correcto. No pude hacer que REQUEST_URL funcionara en la primera RewriteCond, pero cambiarlo por RewriteCond %{REQUEST_FILENAME} .(pdf|zip)$ resolvió el problema.

Ahhh, lo siento, eso fue un error tipográfico, debería ser REQUEST_URI
, no REQUEST_URL
! (He actualizado mi respuesta). ¡Me alegra que lo hayas conseguido funcionar!

Hola, Gracias por tu truco. Tengo una pregunta: si no estoy logueado, me redirige a la página de login cuando pongo una URL con archivo PDF en el navegador. ¿Es posible redirigir a una página 404? Gracias de nuevo.

@sampaii Necesitarías hacer eso en tu script de descarga/autenticación (en PHP)... si el usuario no está logueado, entonces responde con un 404 en lugar de redirigir, lo cual probablemente está siendo activado actualmente por tu script, ¿si has seguido el ejemplo de arriba?
