Proteger acceso directo a PDF y ZIP sin plugin en WordPress

29 sept 2017, 20:54:38
Vistas: 13.6K
Votos: 6

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 &#8212; 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 );
4
Comentarios

Ten en cuenta que si colocas este comentario al inicio de la respuesta /** Plugin Name: Zip Plugin **/ se convierte en un plugin, pero lo que estás preguntando parece no tener nada que ver con WordPress, es una pregunta pura de Apache HTAccess

Tom J Nowell Tom J Nowell
29 sept 2017 21:45:01

Gracias - la publicación original a la que me refería contiene el dl-file.php con la verificación de inicio de sesión / redirección que es específica de WordPress - eso es lo que estoy teniendo problemas para hacer funcionar. Intentando verificar pdf y zip, y que aún funcione la verificación condicional de inicio de sesión para la parte de redirección. He agregado más detalles para mayor claridad.

d38 d38
29 sept 2017 22:47:45

Acabo de notar que cambiaste dl-file.php por filecheck.php? Presumiblemente es el mismo archivo?

MrWhite MrWhite
30 sept 2017 00:54:45

Gracias, es correcto. Lo había cambiado aquí, pero lo mantuve igual en el ejemplo. Pasé por alto mi referencia a ello.

d38 d38
30 sept 2017 01:16:15
Todas las respuestas a la pregunta 1
6
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.

30 sept 2017 00:26:22
Comentarios

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?

d38 d38
30 sept 2017 01:18:28

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.

d38 d38
30 sept 2017 02:32:49

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!

MrWhite MrWhite
30 sept 2017 02:45:39

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.

Samuel Samuel
26 abr 2019 12:01:05

@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?

MrWhite MrWhite
26 abr 2019 12:59:32

mrwhite, disculpa, acabo de ver ahora tu mensaje. Hay un problema con este método. Cuando estoy logueado en el backend y escribo la URL de cualquier archivo PDF, me redirige a la página 404. ¿Alguna idea, por favor?

Samuel Samuel
12 may 2020 16:22:28
Mostrar los 1 comentarios restantes