Jak ograniczyć pobieranie załączników do konkretnego użytkownika?

12

Mam bardzo konkretny przypadek użycia, w którym witryna zbudowana dla prawnika, a każdy z jego klientów może zalogować się do własnej „określonej strony / portalu” (niestandardowy typ postu) bez możliwości dostępu do wp-admin itp. (Stworzyłem wszystkie strony logowania / rejestracji / edycji profilu w interfejsie). Na tej stronie / portalu prawnik pozostawi klientowi wiadomości i pliki do pobrania , teraz teoretycznie jeden klient może odgadnąć (lub jeśli ma wiedzę o plikach innego klienta) inne nazwy plików i pobrać, a tym samym stworzyć problem z prywatnością / bezpieczeństwem / poufne materiały itp.

Szukam pomysłów / koncepcji rozwiązania, początkowo myślałem, że link do pobierania wskazuje na download.php wysyłający identyfikator załącznika, identyfikator użytkownika, identyfikator strony / portalu i nonce, a po drugiej stronie to przetwarzam. .

co myślisz? czy jestem na dobrej drodze, czy to podejście jest wadliwe?

Dzięki!

Amit
źródło
Czy znalazłeś rozwiązanie tego problemu?
brasofilo
@brasofilo, no ..
Amit

Odpowiedzi:

6

To, co musi się zdarzyć, to konieczność przesłania przez proxy żądań pobierania żądanych typów plików za pośrednictwem WordPress. Załóżmy, że ograniczysz dostęp do plików „.doc”.

1. Zdefiniuj zmienną zapytania, która wskazuje żądany plik

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

2. Zaktualizuj .htaccess, aby przekazywać żądania plików zastrzeżonych do WordPress

Spowoduje to przechwycenie żądań do plików, które chcesz ograniczyć, i odesłanie ich z powrotem do WordPress przy użyciu niestandardowej zmiennej zapytania powyżej. Wstaw następującą regułę przed RewriteCondwierszami.

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

3. Przechwyć żądaną nazwę pliku w niestandardowej zmiennej zapytania; i sprawdź dostęp do pliku:

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' );

Uwaga: To rozwiązanie działa tylko w przypadku instalacji w jednym miejscu ! Wynika to z faktu, że WordPress MU już przekazuje przesłane żądania plików w podstronach przez wp-includes/ms-files.php. Istnieje również rozwiązanie dla WordPress MU, ale jest nieco bardziej zaangażowane.

Bendoh
źródło
1
Cześć, nie widzę, żebyś podnosił tę funkcję krok po kroku intercept_file_requestlub że jest ona gdziekolwiek wywoływana, w jaki sposób ta funkcja jest uruchamiana?
Bobz
Dobrze, że należy się przyczepić wp, zaktualizowałem przykład.
Bendoh,
3

Niedawno miałem podobny problem i napisałem o tym artykuł .

Zakładam, że pliki do pobrania są przesyłane za pomocą obsługi multimediów WordPress - w przeciwnym razie masz identyfikator załącznika do pobrania.

Zarys rozwiązania

  • Ustaw katalog przesyłania jako „bezpieczny” (w tym sensie mam na myśli jedynie.htaccess blokowanie wszelkich prób bezpośredniego dostępu do plików w katalogu przesyłania (lub jego podkatalogu) - np. Przez mysite.com/wp-content/uploads/conf/2012/09/myconfidentialfile.pdf)
  • Utwórz link do pobrania zawierający identyfikator załącznika - przechodzi on przez WordPress, aby sprawdzić uprawnienia użytkownika do przeglądania załącznika zezwala / odmawia dostępu.

Ostrzeżenia

  • Wykorzystuje to w .htaccesscelu zapewnienia bezpieczeństwa . Jeśli nie jest to dostępne / włączone (na przykład serwery Nginx), nie uzyskasz większego bezpieczeństwa. Możesz uniemożliwić użytkownikowi przeglądanie katalogu uplods. Ale bezpośredni dostęp będzie działał.
  • Jak wyżej Nie należy tego używać w dystrybucji, jeśli wymaga się absolutnego bezpieczeństwa . Jest w porządku, jeśli twoja konfiguracja działa, ale ogólnie nie można tego zagwarantować. Mój link do artykułu częściowo próbuje rozwiązać ten problem.
  • Utracisz miniatury . Zablokowanie bezpośredniego dostępu do folderu lub podfolderu oznacza, że ​​nie można wyświetlić miniatur plików w tym folderze. Mój link do artykułu częściowo próbuje rozwiązać ten problem.

Blokowanie bezpośredniego dostępu

Aby to zrobić w folderze przesyłania (lub w podfolderze - wszystkie poufne materiały muszą znajdować się na dowolnej głębokości w tym folderze). Umieść .htaccessplik z następującymi danymi:

Order Deny,Allow
Deny from all

Poniżej zakładam, że będziesz dołączać poufne materiały do ​​postu typu „klient”. Wszelkie media przesłane na stronę edycji klienta będą przechowywane w uploads/conf/folderze

Funkcja konfiguracji chronionego katalogu przesyłania

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

Przesyłanie poufnych materiałów

   /**
    * 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' );
                }
        }

    }

Po wykonaniu tej czynności przesłane treści powinny znajdować się w środku, uploads/confa próba uzyskania do nich bezpośredniego dostępu za pomocą przeglądarki nie powinna działać.

Pobieranie zawartości

To jest łatwe. Adres URL pobierania może być czymś www.site.com?wpse26342download=5(gdzie 5 to identyfikator załącznika przesłanej treści). Używamy tego do identyfikacji załącznika, sprawdzania uprawnień bieżącego użytkownika i umożliwienia mu pobrania.

Najpierw skonfiguruj zmienną zapytania

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

Teraz skonfiguruj detektor, aby (być może) uruchamiał pobieranie ...

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

Komentarze końcowe

Powyższy kod może zawierać błędy / błędy składniowe i jest nieprzetestowany i używasz go na własne ryzyko :).

Adres URL pobierania można „wstępnie zapisać” za pomocą przepisywania. Jak stwierdzono w komentarzach, możesz dodać puste miejsce index.phpw każdym podrzędnym chronionym folderze, aby zapobiec przeglądaniu - ale i tak powinny temu zapobiec .htaccessreguły.

Bardziej bezpieczną metodą byłoby przechowywanie plików publicznych poza katalogiem publicznym. Lub w usłudze zewnętrznej, takiej jak Amazon S3. W przypadku tego ostatniego musisz wygenerować prawidłowy adres URL, aby pobrać plik z Amazon (używając klucza prywatnego). Oba wymagają pewnego poziomu zaufania do usług hosta / strony trzeciej.

Byłbym ostrożny wobec używania jakichkolwiek wtyczek, które sugerują, że oferują „chronione pliki do pobrania”. Nie znalazłem żadnych, które zapewniają wystarczająco dobre bezpieczeństwo. Proszę również nie brać pod uwagę tego rozwiązania - i chętnie przyjmę wszelkie sugestie lub krytykę.

Stephen Harris
źródło
1

Prawdopodobnie znasz tę sztuczkę. Ten kod sprawdzi bieżącą zalogowaną nazwę użytkownika i jeśli się zgadza, wyświetli link do pobrania tego pliku, w przeciwnym razie nic nie pokaże.

oto kod:

<?php 
    global $current_user;
    get_currentuserinfo();

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

Nie będzie to jednak dobre podejście, ponieważ pliki są przechowywane na serwerach, każdy z linkiem może pobrać ten plik.

amit
źródło
0

Zakładam, że te informacje są poufne i dlatego oprócz ukrywania linków do plików, naprawdę chcesz, aby były one całkowicie niedostępne dla kogokolwiek w sieci, nawet jeśli zgadną adres URL, chyba że użytkownik ma wyraźne pozwolenie na pobranie pliki.

Sprawdź, jak bezpiecznie przechowywać pliki w Amazon S3, a następnie podaj do pliku wstępnie podpisane (ograniczone czasowo) adresy URL, pod warunkiem, że zostały spełnione prawidłowe kontrole bezpieczeństwa (tj. Użytkownik zalogował się na Twojej stronie i jest tym, za kogo się podaje).

Istnieje bardzo dobry zestaw SDK AWS, który sprawia, że ​​jest to bardzo proste.

Musisz zbadać, w jaki sposób wysyłać pliki przesłane przez interfejs przesyłania WP do S3, zamiast tego zbudować własny program do przesyłania .

Inną opcją byłoby zbyt zajrzenie do kodu WP e-commerce . Oferują bezpieczne pobieranie plików oprogramowania (np. MP3). Uważam, że pliki są konwertowane na skróty za pomocą klucza szyfrowania, który jest generowany na użytkownika przy zakupie. To zajmie trochę odszyfrowania, aby zobaczyć, jak to działa, ale proces nie będzie unikalny dla tej wtyczki, więc inne przykłady będą dostępne (gdzieś).

deadlyhifi
źródło
0

Myślę, że szyfrowanie plików jest sposobem, aby przejść jak powyższa odpowiedź. Na Wordpress.org znajduje się wtyczka, która pozwala chronić pobierane pliki. http://wordpress.org/extend/plugins/download-protect/ Możesz także skorzystać z usługi Amazon lub dysku Google. Istnieje wiele usług oferujących chronione pliki do pobrania, takie jak drop box.

Chris
źródło
bezpieczeństwo przez zaciemnienie jest złym podejściem. każdy będzie mógł zobaczyć żądanie HTTP i uzyskać adres URL w ten sposób.
mulllhausen