Como restringir o download de anexos a um usuário específico?

12

Eu tenho um caso de uso muito específico em que o site criado para um advogado e cada um de seus clientes pode fazer login em sua própria 'página / portal específico' (tipo de postagem personalizado) sem a capacidade de acessar o wp-admin etc. (criei todos os páginas de login / registro / edição de perfil no front-end). Nesta página / portal, o advogado deixará mensagens e arquivos para download do cliente , agora teoricamente falando, um cliente pode adivinhar (ou se tiver conhecimento dos arquivos de outro cliente) outros nomes de arquivo e fazer o download, criando um problema de privacidade / segurança / material confidencial etc.

Estou procurando idéias / conceitos para uma solução, meu pensamento inicial era ter o link de download apontado para algum download.php enviando o ID do anexo, o ID do usuário, o ID da página / portal e o nonce e, por outro lado, processando isso. .

O que você acha? Estou no caminho certo ou esta abordagem é falha?

Obrigado!

Amit
fonte
Você encontrou uma solução para isso?
Brasofilo
@brasofilo, no ..
Amit

Respostas:

6

O que precisa acontecer é que você precisa solicitar solicitações de download por proxy para os tipos de arquivos que deseja através do WordPress. Vamos supor que você restrinja o acesso a arquivos ".doc".

1. Defina uma variável de consulta que indica o arquivo solicitado

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

2. Atualize .htaccess para encaminhar solicitações de arquivos restritos ao WordPress

Isso capturará solicitações para os arquivos que você deseja restringir e as enviará de volta ao WordPress usando a variável de consulta personalizada acima. Insira a seguinte regra antes das RewriteCondlinhas.

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

3. Capture o nome do arquivo solicitado na variável de consulta personalizada; e verifique o acesso ao arquivo:

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 solução funciona apenas para instalações de site único ! Isso ocorre porque o MU do WordPress já encaminha solicitações de arquivos carregados em sub-sites wp-includes/ms-files.php. Também existe uma solução para o MU do WordPress, mas é um pouco mais envolvida.

Bendoh
fonte
Olá, não vejo que você conecte esta função na etapa intercept_file_requestou que esteja sendo chamada em qualquer lugar, como essa função é acionada?
Bobz
Bom ponto, ele deve estar ligado wp, atualizei o exemplo.
Bendoh
3

Recentemente, tive um problema relacionado e escrevi este artigo sobre ele .

Suponho que os downloads sejam enviados por meio da manipulação de mídia do WordPress - ou você tem um ID de anexo para o download.

Esboço da solução

  • Torne o diretório de uploads 'seguro' (nesse sentido, apenas quero dizer usar .htaccesspara bloquear qualquer tentativa de acessar diretamente os arquivos no diretório de uploads (ou um subdiretório) - por exemplo, via mysite.com/wp-content/uploads/conf/2012/09/myconfidentialfile.pdf)
  • Crie um link para download, incluindo o ID do anexo - isso passa pelo WordPress para verificar a permissão do usuário para visualizar o anexo permite / nega acesso.

Ressalvas

  • Isso faz uso .htaccesspara fornecer segurança . Se isso não estiver disponível / ativado (servidores nginx, por exemplo), você não terá muita segurança. Você pode impedir que o usuário navegue no diretório uplods. Mas o acesso direto funcionará.
  • Conforme acima. Isso não deve ser usado na distribuição se você precisar de segurança absoluta . Tudo bem se sua configuração específica funcionar - mas, em geral, não pode ser garantida. Meu artigo vinculado está em parte tentando resolver isso.
  • Você perderá miniaturas . Bloquear o acesso direto a uma pasta ou subpasta significa que as miniaturas dos arquivos dessa pasta não podem ser exibidas. Meu artigo vinculado está em parte tentando resolver isso.

Bloqueando o acesso direto

Para fazer isso na sua pasta de uploads (ou em uma subpasta - todo o material confidencial deve residir, a qualquer profundidade, dentro desta pasta). Coloque um .htaccessarquivo com o seguinte:

Order Deny,Allow
Deny from all

A seguir, suponho que você anexará material confidencial ao tipo de postagem 'cliente'. Qualquer mídia carregada na página de edição do cliente será armazenada na uploads/conf/pasta

A função para configurar o diretório de uploads protegidos

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.
}

Upload de 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' );
                }
        }

    }

Feito isso, o conteúdo carregado deve estar dentro uploads/confe a tentativa de acessá-lo diretamente usando o navegador não deve funcionar.

Download de conteúdo

Isso é facil. O URL do download pode ser algo www.site.com?wpse26342download=5(em que 5 é o ID do anexo do conteúdo carregado). Usamos isso para identificar o anexo, verificar as permissões do usuário atual e permitir o download.

Primeiro, configure a variável 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;
}}

Agora configure um ouvinte para (talvez) acionar o download ...

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;
}

Comentários finais

O código acima pode conter erros de bugs / sintaxe e não foi testado, e você o utiliza por sua própria conta e risco :).

O URL do download pode ser "pré-modificado" usando reescritas. Conforme declarado nos comentários, você pode adicionar um espaço index.phpem branco dentro de cada filho da pasta protegida para impedir a navegação - mas isso deve ser evitado pelas .htaccessregras de qualquer maneira.

Um método mais seguro seria armazenar os arquivos públicos fora de um diretório público. Ou em um serviço externo como o Amazon S3. Para o último, você precisará gerar um URL válido para buscar o arquivo da Amazon (usando sua chave privada). Ambos exigem um certo nível de confiança no seu host / serviço de terceiros.

Eu ficaria cauteloso ao usar quaisquer plug-ins que sugiram que eles oferecem 'downloads protegidos'. Não encontrei nenhum que ofereça segurança suficiente. Por favor, não as advertências desta solução também - e eu gostaria de receber sugestões ou críticas.

Stephen Harris
fonte
1

Provavelmente, você já deve ter conhecido esse truque. Esse código verificará o nome de usuário do usuário conectado no momento e, se ele corresponder, exibirá o link de download para esse arquivo, caso contrário, não mostrará nada.

aqui está o código:

<?php 
    global $current_user;
    get_currentuserinfo();

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

No entanto, essa não será uma boa abordagem, como os arquivos são armazenados nos servidores, qualquer pessoa com link pode fazer o download desse arquivo.

amit
fonte
0

Suponho que essas informações sejam confidenciais e, portanto, além de ocultar os links para os arquivos, você deseja torná-los inacessíveis completamente a qualquer pessoa na web, mesmo que adivinhem o URL, a menos que o usuário tenha permissão explícita para fazer o download os arquivos.

Examine o armazenamento seguro dos arquivos no Amazon S3 e, em seguida, forneça URLs pré-assinados (com tempo limitado) para o arquivo, desde que as verificações de segurança corretas tenham sido atendidas (por exemplo, o usuário fez login no site e é quem eles dizem que são).

Há um SDK da AWS muito bom que torna muito simples fazer isso.

O que você precisará pesquisar é como enviar arquivos enviados pela interface de upload do WP para o S3, em vez disso, criar o seu próprio remetente .

Outras opções também seriam procurar o código do WP e-commerce . Eles oferecem download seguro de arquivos de software (por exemplo, MP3s). Acredito que os arquivos sejam convertidos em hashes com uma chave de criptografia gerada por usuário na compra. Isso levaria uma certa decifração para ver como funcionava, mas o processo não será exclusivo desse plug-in, portanto outros exemplos estarão disponíveis (em algum lugar).

deadlyhifi
fonte
0

Eu acho que a criptografia dos arquivos é o caminho a seguir, como a resposta acima. Existe um plugin no Wordpress.org que permite proteger os downloads. http://wordpress.org/extend/plugins/download-protect/ Você também pode usar o serviço amazons ou o google drive. Existem muitos serviços que oferecem downloads protegidos, como o drop box.

Chris
fonte
segurança através da obscuridade é uma má abordagem. qualquer pessoa poderá ver a solicitação http e obter o URL dessa maneira.
14Bom