¿Cómo restringir la descarga de archivos adjuntos a un usuario específico?

12

Tengo un caso de uso muy específico en el que el sitio creado para un abogado y cada uno de sus clientes pueden iniciar sesión en su propia "página / portal específico" (tipo de publicación personalizada) sin la capacidad de acceder a wp-admin, etc. (Creé todos los páginas de inicio de sesión / registro / edición de perfil en el front-end). En esta página / portal, el abogado dejará mensajes y archivos para que el cliente los descargue , ahora teóricamente hablando, un cliente puede adivinar (o si tiene conocimiento de los archivos de otro cliente) otros nombres de archivos y descargarlos, creando así un problema con la privacidad / seguridad / material confidencial, etc.

Estoy buscando ideas / conceptos para una solución, mi pensamiento inicial fue que el enlace de descarga apuntara a algún download.php enviando la identificación del archivo adjunto, la identificación del usuario, la identificación de la página / portal y nonce y, por otro lado, procesar eso. .

¿Qué piensas? ¿Estoy en el camino correcto o este enfoque es defectuoso?

¡Gracias!

Amit
fuente
¿Has encontrado una solución para esto?
brasofilo
@brasofilo, no ..
Amit

Respuestas:

6

Lo que debe suceder es que debe enviar solicitudes de descarga de proxy para los tipos de archivos que desea a través de WordPress. Supongamos que va a restringir el acceso a los archivos ".doc".

1. Defina una variable de consulta que indique el archivo solicitado

function add_get_file_query_var( $vars ) {
    $vars[] = 'get_file';
    return $vars;
}
add_filter( 'query_vars', 'add_get_file_query_var' );

2. Actualice .htaccess para reenviar solicitudes de archivos restringidos a WordPress

Esto capturará las solicitudes a los archivos que desea restringir y las enviará de vuelta a WordPress utilizando la variable de consulta personalizada anterior. Inserte la siguiente regla antes de las RewriteCondlíneas.

RewriteRule ^wp-content/uploads/(.*\.docx)$ /index.php?get_file=$1

3. Capture el nombre del archivo solicitado en la variable de consulta personalizada; y verifique el acceso al archivo:

function intercept_file_request( $wp ) {
    if( !isset( $wp->query_vars['get_file'] ) )
        return;

    global $wpdb, $current_user;

    // Find attachment entry for this file in the database:
    $query = $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE guid='%s'", $_SERVER['REQUEST_URI'] );
    $attachment_id = $wpdb->get_var( $query );

    // No attachment found. 404 error.  
    if( !$attachment_id ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Get post from database 
    $file_post = get_post( $attachment_id );
    $file_path = get_attached_file( $attachment_id );

    if( !$file_post || !$file_path || !file_exists( $file_path ) ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Logic for validating current user's access to this file...
    // Option A: check for user capability
    if( !current_user_can( 'required_capability' ) ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Option B: check against current user
    if( $current_user->user_login == "authorized_user" ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Everything checks out, user can see this file. Simulate headers and go:
    header( 'Content-Type: ' . $file_post->post_mime_type );
    header( 'Content-Dispositon: attachment; filename="'. basename( $file_path ) .'"' );
    header( 'Content-Length: ' . filesize( $file_path ) );

    echo file_get_contents( $file_path );
    die(0);
}
add_action( 'wp', 'intercept_file_request' );

Nota: ¡ Esta solución funciona solo para instalaciones de un solo sitio ! Esto se debe a que WordPress MU ya reenvía solicitudes de archivos cargados en subsitios a través de wp-includes/ms-files.php. También hay una solución para WordPress MU, pero es un poco más complicada.

Bendoh
fuente
1
Hola, no veo que enganches esta función en el paso intercept_file_requesto que se llame a alguna parte, ¿cómo se activa esa función?
Bobz el
Buen punto, debería engancharse wp, he actualizado el ejemplo.
Bendoh
3

Recientemente tuve un problema relacionado y escribí este artículo al respecto .

Asumiré que las descargas se cargan a través del manejo de medios de WordPress, o de lo contrario tiene una ID de archivo adjunto para la descarga.

Esquema de la solución

  • Hacer que el directorio de archivos 'seguro' (en este sentido que acaba de utilización media .htaccessde bloquear cualquier intento de acceso de los archivos directamente en el directorio de archivos (o un subdirectorio del mismo) - por ejemplo, a través de mysite.com/wp-content/uploads/conf/2012/09/myconfidentialfile.pdf)
  • Cree un enlace de descarga que incluya la ID del archivo adjunto: esto pasa por WordPress para verificar que el permiso del usuario para ver el archivo adjunto permite / niega el acceso.

Advertencias

  • Esto se utiliza .htaccesspara proporcionar seguridad . Si esto no está disponible / activado (servidores nginx, por ejemplo), entonces no obtendrá mucha seguridad. Puede evitar que el usuario explore el directorio uplods. Pero el acceso directo funcionará.
  • Según lo anterior. Esto no debe usarse en la distribución si requiere seguridad absoluta . Está bien si su configuración específica funciona, pero en general, no se puede garantizar. Mi artículo vinculado está tratando en parte de abordar esto.
  • Perderás miniaturas . Bloquear el acceso directo a una carpeta o subcarpeta significará que no se pueden ver las miniaturas de los archivos en esa carpeta. Mi artículo vinculado intenta en parte abordar esto.

Bloqueo de acceso directo

Para hacer esto en su carpeta de cargas (o una subcarpeta, todo el material confidencial debe residir, a cualquier profundidad, dentro de esta carpeta). Coloque un .htaccessarchivo con lo siguiente:

Order Deny,Allow
Deny from all

A continuación, supongo que adjuntará material confidencial para publicar el tipo 'cliente'. Cualquier medio cargado en la página de edición del cliente se almacenará en la uploads/conf/carpeta

La función para configurar el directorio de cargas protegidas

function wpse26342_setup_uploads_dir(){

    $wp_upload_dir = wp_upload_dir();
    $protected_folder = trailingslashit($wp_upload_dir['basedir']) . 'conf';    

    // Do not allow direct access to files in protected folder
    // Add rules to /uploads/conf/.htacess
    $rules = "Order Deny,Allow\n";
    $rules .= "Deny from all";

    if( ! @file_get_contents( trailingslashit($protected_folder).'.htaccess' ) ) {
            //Protected directory doesn't exist - create it.
        wp_mkdir_p( $protected_folder);
    }
    @file_put_contents( trailingslashit($protected_folder).'.htaccess', $rules );

     //Optional add blank index.php file to each sub-folder of protected folder.
}

Subir material confidencial

   /**
    * Checks if content is being uploaded on the client edit-page
    * Calls a function to ensure the protected file has the .htaccess rules
    * Filters the upload destination to the protected file
    */
    add_action('admin_init', 'wpse26342_maybe_change_uploads_dir', 999);
    function wpse26342_maybe_change_uploads_dir() {
        global $pagenow;

        if ( ! empty( $_POST['post_id'] ) && ( 'async-upload.php' == $pagenow || 'media-upload.php' == $pagenow ) ) {
                if ( 'client' == get_post_type( $_REQUEST['post_id'] ) ) {
                       //Uploading content on the edit-client page

                       //Make sure uploads directory is protected
                       wpse26342_setup_uploads_dir();

                       //Change the destination of the uploaded file to protected directory.
                       add_filter( 'upload_dir', 'wpse26342_set_uploads_dir' );
                }
        }

    }

Una vez hecho esto, el contenido cargado debería estar dentro uploads/confy no debería funcionar intentar acceder directamente a él utilizando su navegador.

Descargando contenido

Esto es facil. La URL de descarga puede ser algo www.site.com?wpse26342download=5(donde 5 es la ID de archivo adjunto del contenido cargado). Usamos esto para identificar el archivo adjunto, verificar los permisos del usuario actual y permitir que se descarguen.

Primero, configure la variable de consulta

/**
 * Adds wpse26342download to the public query variables
 * This is used for the public download url
 */
add_action('query_vars','wpse26342_add_download_qv');
function wpse26342_add_download_qv( $qv ){
    $qv[] = 'wpse26342download';
    return $qv;
}}

Ahora configure un oyente para (quizás) activar la descarga ...

add_action('request','wpse26342_trigger_download');
function wpse26342_trigger_download( $query_vars ){

        //Only continue if the query variable set and user is logged in...
    if( !empty($query_vars['wpse26342download']) && is_user_logged_in() ){

        //Get attachment download path
        $attachment = (int) $query_vars['wpse26342download'];
        $file = get_attached_file($attachment);

        if( !$file )
             return;

        //Check if user has permission to download. If not abort.       
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='.basename($file));
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));

        ob_clean();
        flush();
        readfile($file);
        exit();
    }
    return $query_vars;
}

Comentarios finales

El código anterior puede contener errores / errores de sintaxis y no se ha probado, y lo usa bajo su propio riesgo :).

La URL de descarga se puede 'prettificar' usando reescrituras. Como se indica en los comentarios, puede agregar un espacio index.phpen blanco dentro de cada elemento secundario de la carpeta protegida para evitar la exploración, pero esto debería evitarse de .htaccesstodas formas.

Un método más seguro sería almacenar los archivos públicos fuera de un directorio público. O en un servicio externo como Amazon S3. Para esto último, deberá generar una URL válida para recuperar el archivo de Amazon (utilizando su clave privada). Ambos requieren un cierto nivel de confianza en su servicio de Host / tercero.

Sería cauteloso sobre el uso de complementos que sugieran que ofrecen 'descargas protegidas'. No he encontrado ninguno que brinde suficiente seguridad. Por favor, no las advertencias de esta solución también, y agradecería cualquier sugerencia o crítica.

Stephen Harris
fuente
1

Probablemente, es posible que haya conocido este truco. Este código verificará el nombre de usuario actual del usuario conectado y si coincide, mostrará el enlace de descarga a ese archivo, de lo contrario no mostrará nada.

Aquí está el código:

<?php 
    global $current_user;
    get_currentuserinfo();

    if ( 'username' == $current_user->user_login ) {
        echo 'Download Link';
    } else {
        // nothing
    }
?>

Sin embargo, este no será un buen enfoque, ya que los archivos se almacenan en servidores, cualquier persona con enlace puede descargar ese archivo.

amit
fuente
0

Supongo que esta información es confidencial y, por lo tanto, además de ocultar los enlaces a los archivos, querrá hacerlos completamente inaccesibles para cualquier persona en la web, incluso si adivinaran la URL, a menos que el usuario tenga permiso explícito para descargar Los archivos.

Busque almacenar los archivos en Amazon S3 de forma segura y luego proporcione URL pre-firmadas (tiempo limitado) al archivo siempre que se hayan satisfecho las comprobaciones de seguridad correctas (es decir, el usuario ha iniciado sesión en su sitio y es quien dice ser).

Hay un muy buen AWS SDK que hace que sea muy sencillo hacer esto.

Lo que necesitará investigar es cómo enviar archivos cargados a través de la interfaz de carga de WP a S3 en su lugar, alternativamente, cree su propio cargador .

Otra opción sería mirar el código del comercio electrónico de WP . Ofrecen descargas seguras de archivos de software (por ejemplo, MP3). Creo que los archivos se convierten en hashes con una clave de cifrado que se genera por usuario en la compra. Esto llevaría un poco de descifrado para ver cómo funcionaba, pero el proceso no será exclusivo de este complemento, por lo que habrá otros ejemplos disponibles (en algún lugar).

mortalhifi
fuente
0

Creo que el cifrado de los archivos es el camino a seguir como la respuesta anterior. Hay un complemento en Wordpress.org que le permite proteger las descargas. http://wordpress.org/extend/plugins/download-protect/ También podría usar el servicio amazons o google drive. Hay muchos servicios que ofrecen descargas protegidas, como Drop Box también.

Chris
fuente
La seguridad a través de la oscuridad es un mal enfoque. cualquiera podrá ver la solicitud http y obtener la url de esta manera.
mulllhausen