¿Cómo proteger las subidas si el usuario no está logueado?

22 dic 2011, 18:46:18
Vistas: 88.3K
Votos: 107

Uso WordPress para un sitio privado donde los usuarios suben archivos. Utilizo "Private WordPress" para evitar el acceso al sitio si el usuario no ha iniciado sesión.

Me gustaría hacer lo mismo con los archivos subidos en la carpeta uploads.

Así, si un usuario no está logueado no podrá acceder a: https://xxxxxxx.com/wp-content/uploads/2011/12/xxxxxxx.pdf si intentan acceder pero no están logueados, deberían ser redirigidos a la página de login, por ejemplo.

Encontré un plugin llamado private files pero su última actualización fue en 2009 y no parece funcionar en mi WordPress.

¿Alguien conoce algún método? ¿El método de hotlinking sería suficiente para proteger esto?

También encontré este método:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^.*uploads/private/.*
RewriteCond %{HTTP_COOKIE} !^.*wordpress_logged_in.*$ [NC]
RewriteRule . /index.php [R,L]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Pero entonces cualquier usuario que replique la cookie podría superar esto, ¿verdad? Saludos

3
Comentarios

¿Alguna razón por la que no puedas usar un directorio de carga diferente, como uno fuera de la raíz del sitio?

onetrickpony onetrickpony
22 dic 2011 19:11:33

No realmente, pero ya tengo toneladas de archivos adjuntos a publicaciones en ese directorio, no me importa mover todo si puedo encontrar una solución adecuada

chifliiiii chifliiiii
22 dic 2011 19:37:52

En caso de que redirijas automáticamente a los usuarios a la pantalla de inicio de sesión, una forma fácil de proteger las cargas es verificar el referente. Si el referente está vacío (acceso directo) o es diferente al dominio donde alojas los archivos, entonces lo bloqueas.

Konstantinos Konstantinos
31 ene 2020 19:55:38
Todas las respuestas a la pregunta 4
11
113

Comprobar solamente si la cookie existe no es una protección muy estricta.

Para obtener una protección más fuerte, puedes pasar o "proxificar" todas las solicitudes a la carpeta de subidas (ejemplificada como uploads en el siguiente ejemplo) a través de un script PHP:

RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]

Todas las solicitudes a archivos subidos (lo que incluye imágenes en las publicaciones) irían a dl-file.php que luego puede verificar si el usuario está conectado o no.

Si el usuario no ha iniciado sesión, se mostrará el formulario de acceso de tu sitio. Después de que el usuario inicie sesión, será redirigido de vuelta al archivo y podrá descargarlo.

Ejemplo de dl-file.php.

Algo similar se puede encontrar en \wp-includes\ms-files.php en tu instalación de WordPress, pero ese es para multisitio y sin la verificación de inicio de sesión y redirecciones.

Dependiendo del tráfico que tengas, podría ser conveniente integrar esto mejor con tu servidor, por ejemplo con cabeceras X-Accel-Redirect o X-Sendfile.

2 ene 2012 23:50:59
Comentarios

¿cómo ajustas dl-file.php si quiero almacenar archivos en un subdirectorio como wp-content/uploads/secure?

User User
12 ene 2012 00:54:57

Esta es la única solución realmente segura. Cualquier otra cosa que puedas encontrar en la web, como verificar el encabezado referer, verificar cookies, deshabilitar el listado de directorios, es una medida a medias ya que puedes falsificar fácilmente los encabezados de solicitudes HTTP para evitarlo.

Lukasz Lukasz
20 sept 2013 11:58:28

Chicos... esta parecía la solución perfecta para mí... el problema es que estoy usando PDFJS de Mozilla para acceder a algunos PDFs desde la carpeta de uploads, y PDFJS usa encabezados partial-content para obtener solo las páginas que le interesan... así que esta solución no me sirve.

¿alguna sugerencia?

Otto Nascarella Otto Nascarella
28 nov 2014 19:24:06

@OttoNascarella: Las solicitudes de contenido parcial a PHP se han resuelto a partir de hoy, esto es independiente de esta pregunta sobre WordPress. De hecho, la pregunta es bastante antigua ya: ¿Descargas resumibles al usar PHP para enviar el archivo?

hakre hakre
5 dic 2014 12:17:49

@hakre ¿Qué pasa con algunas de esas imágenes utilizadas en la página principal del sitio web cuando cualquier usuario visita el sitio?

Me da un error 404 si no estoy logueado.

Dhaval Panchal Dhaval Panchal
1 may 2018 11:30:58

¿Podría alguien explicar esta línea de código list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL); y en qué se diferencia de $basedir = wp_upload_dir()['basedir']?

Tomas Eklund Tomas Eklund
2 abr 2020 14:02:30

@TomasEklund: El tuyo probablemente es menos complicado. Una razón para el original es muy probablemente la compatibilidad con versiones anteriores de PHP.

hakre hakre
4 abr 2020 17:08:42

Genial. ¿Alguien podría por favor convertir esta solución en un plugin de WordPress?

Jian Chen Jian Chen
17 may 2020 07:17:34

Nota al margen: funciona en WordPress 5.6.

long long
4 mar 2021 15:17:33

En mi configuración Multisite, tuve problemas para completar la ubicación del archivo con el código proporcionado. Esto funcionó mejor: $file = rtrim( DIR, '/' ) . $_SERVER['REQUEST_URI'];

Talha Imam Talha Imam
29 jul 2021 22:40:29

Para nginx en el archivo de configuración del sitio, dentro del bloque server{}, pude agregar lo siguiente para que funcione: location /wp-content/uploads/ { rewrite ^/wp-content/uploads/(.*)$ /dl-file.php?file=$1 last; }

hax2024 hax2024
12 abr 2022 00:23:31
Mostrar los 6 comentarios restantes
4
21

Dos formas, la simple en 2. con ayuda de una regla de Apache o en 1. con ayuda de código personalizado en un plugin.

1. Plugin

Puedes escribir un plugin usando el hook init y el valor GET $_GET[ 'file' ];. Si el usuario tiene este valor GET, saltar a una función para verificar los permisos de acceso a los archivos: Por ejemplo, con un checkbox dentro de un Meta Box.

add_action( 'init', 'fb_init' );
function fb_init() {
    // esto en una función para el hook init
    if ( '' != $_GET[ 'file' ] ) {
        fb_get_file( $_GET[ 'file' ] );
    }
}

la función fb_get_file()

function fb_get_file( $file ) {

    $upload     = wp_upload_dir();
    $the_file   = $file; 
    $file       = $upload[ 'basedir' ] . '/' . $file;
    if ( !is_file( $file ) ) {
        status_header( 404 );
        die( '404 &#8212; Archivo no encontrado.' );
    }
    else {
        $image = get_posts( array( 'post_type' => 'attachment', 'meta_query' => array( array( 'key' => '_wp_attached_file', 'value' => $the_file ) ) ) );
        if ( 0 < count( $image ) && 0 < $image[0] -> post_parent ) { // adjunto encontrado y padre disponible
            if ( post_password_required( $image[0] -> post_parent ) ) { // contraseña para el post no disponible
                wp_die( get_the_password_form() );// mostrar el formulario de contraseña 
            }
            $status = get_post_meta( $image[0] -> post_parent, '_inpsyde_protect_content', true );

            if ( 1 == $status &&  !is_user_logged_in() ) {
                wp_redirect( wp_login_url( $upload[ 'baseurl' ] . '/' . $the_file ) );
                die();
            }
        }
        else {
            // no es un adjunto normal, verificar miniatura
            $filename   = pathinfo( $the_file );
            $images     = get_posts( array( 'post_type' => 'attachment', 'meta_query' => array( array( 'key' => '_wp_attachment_metadata', 'compare' => 'LIKE', 'value' => $filename[ 'filename' ] . '.' . $filename[ 'extension' ] ) ) ) );
            if ( 0 < count( $images ) ) {
                foreach ( $images as $SINGLEimage ) {
                    $meta = wp_get_attachment_metadata( $SINGLEimage -> ID );
                    if ( 0 < count( $meta[ 'sizes' ] ) ) {
                        $filepath   = pathinfo( $meta[ 'file' ] );
                        if ( $filepath[ 'dirname' ] == $filename[ 'dirname' ] ) {// ruta actual de la miniatura
                            foreach ( $meta[ 'sizes' ] as $SINGLEsize ) {
                                if ( $filename[ 'filename' ] . '.' . $filename[ 'extension' ] == $SINGLEsize[ 'file' ] ) {
                                    if ( post_password_required( $SINGLEimage -> post_parent ) ) { // contraseña para el post no disponible
                                        wp_die( get_the_password_form() );// mostrar el formulario de contraseña 
                                    }
                                    die('dD');
                                    $status = get_post_meta( $SINGLEimage -> post_parent, '_inpsyde_protect_content', true );

                                    if ( 1 == $status &&  !is_user_logged_in() ) {
                                        wp_redirect( wp_login_url( $upload[ 'baseurl' ] . '/' . $the_file ) );
                                        die();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    $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 la cadena está vacía, devolver 0. Si no, intentar convertir a timestamp
    $client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;

    // Crear 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 servir el archivo
    readfile( $file );
    die();
}

También puedes agregar una URL personalizada para archivos mediante el hook generate_rewrite_rules

add_filter( 'generate_rewrite_rules', 'fb_generate_rewrite_rules' );

function fb_generate_rewrite_rules( $wprewrite ) {
        $upload = wp_upload_dir();
        $path = str_replace( site_url( '/' ), '', $upload[ 'baseurl' ] );
        $wprewrite -> non_wp_rules = array( $path . '/(.*)' => 'index.php?file=$1' );
        return $wprewrite;
}

2. Verificación de Cookie con Apache

Deja un nuevo archivo .htaccess dentro del directorio /wp-content/uploads/. U otro directorio definido para las subidas.

Cómo funciona

Dentro de los contenedores <IfModule>, hay tres reglas que hacen lo siguiente:

  1. Verifica si la solicitud es para cualquier archivo
  2. Verifica la ausencia de una cookie que comience con wordpress_logged_in_
  3. Si se cumplen estas condiciones, la solicitud del archivo será denegada mediante una respuesta 403 "Prohibido"

El truco aquí es el paso 2, verificar la ausencia de una cookie que comience con wordpress_logged_in_. Cuando el usuario inicia sesión, WordPress agrega una cookie a tu navegador que luce como:

wordpress_logged_in_1234567890abcdefghijklmnopqrstuvwxyz

Ejemplo de regla con verificación de tipo de archivo

# requerir inicio de sesión para archivos multimedia
<IfModule mod_rewrite.c>
    RewriteCond %{REQUEST_FILENAME} (.*)
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_([a-zA-Z0-9_]*) [NC]
    RewriteRule .* - [F,L]
</IfModule>
3 ene 2012 12:20:25
Comentarios

Esto no funcionó en mi caso, ¿alguien sabe por qué? Lo copié exactamente igual.

Ryan S Ryan S
17 feb 2014 12:40:26

La protección solo funciona con PDF. No funciona con otras extensiones de archivo como: doc, docx, jpg, etc...

Patel Patel
23 ago 2017 13:43:48

La opción 2 funcionó perfectamente para todos los tipos de archivo, gracias.

crdunst crdunst
5 mar 2021 18:23:21

¿Por qué el condicional para el tamaño de archivo en IIS?

Scribblemacher Scribblemacher
11 mar 2025 13:34:09
1

Si prefieres un enfoque basado en plugins para resolver este problema, aquí hay una solución bastante buena que he encontrado (finalmente):

  1. Instala el plugin 'Download Monitor', disponible en:
    https://wordpress.org/plugins/download-monitor/
  2. En el Escritorio de WordPress, ve al nuevo elemento de menú 'Descargas' y añade una nueva 'Descarga', como se describe en la documentación del plugin aquí: https://www.download-monitor.com/kb/adding-downloads/. Toma nota del shortcode de 'Descarga' que se te proporciona (por ejemplo, guárdalo en el Bloc de notas). Ten en cuenta que el archivo se guarda en /wp-content/uploads/dlm_uploads/
  3. En el metabox 'Opciones de descarga', especifica 'Solo para miembros' (como se documenta aquí https://www.download-monitor.com/kb/download-options/), y haz clic en 'Publicar'.
  4. En la página donde quieras que aparezca la descarga solo para miembros, añade el shortcode que anotaste en el paso #2, y 'Publica/Actualiza' la página, como se documenta aquí: https://www.download-monitor.com/kb/shortcode-download/. Puedes cambiar la plantilla del enlace de descarga como se describe aquí https://www.download-monitor.com/kb/content-templates/, o crear tu propia plantilla (por ejemplo, para eliminar el 'contador' de descargas)
  5. Navega a tu página, deberías ver un enlace de descarga (pero que no revela la URL del archivo). Si navegas a la misma página en una nueva ventana del navegador (o en modo Incógnito), deberías ver que la descarga ya no funciona.

Esto significa que cualquier persona que no haya iniciado sesión no puede descargar el archivo ni ver la URL real del archivo. En el caso de que alguien no autorizado descubra la URL del archivo, el plugin también evita que los usuarios accedan a la URL real del archivo bloqueando el acceso a la carpeta /wp-content/uploads/dlm_uploads/.

Bonus: si estás haciendo esto para un sitio donde necesitas que los usuarios puedan iniciar sesión solo como 'Miembros' (pero sin permisos de WordPress como editar páginas o ser Administrador), instala el plugin 'Members' https://wordpress.org/plugins/members/, crea un nuevo rol de usuario llamado 'Miembro', y asígnale la única capacidad de 'leer', crea un nuevo Usuario en WordPress, y asegúrate de asignarle el rol de 'Miembro'.

Si quieres proteger el contenido de las páginas, el plugin 'Members' proporciona algunas opciones, o hay otros plugins disponibles. Si quieres personalizar la página de inicio de sesión para que los Miembros tengan una apariencia mejor que el formulario de inicio de sesión predeterminado de WordPress, usa algo como 'Theme My Login': https://wordpress.org/plugins/theme-my-login/

13 abr 2017 12:09:36
Comentarios

El proceso que he descrito anteriormente también se explica aquí, aunque como puedes ver no tiene que ser específico solo para PDFs: http://www.thedigitalcrowd.com/website-development/wordpress/wordpress-restrict-access-to-pdf-downloads-with-private-members-login-details/

Matty J Matty J
13 abr 2017 12:11:01
0

¿Qué tal un enfoque basado en plugins para resolver este problema? He encontrado un plugin de WordPress creado para abordar esto:

Prevenir acceso a archivos/carpetas: https://wordpress.org/plugins/prevent-file-access/

8 ene 2021 16:28:32