Защита прямого доступа к PDF и ZIP без плагинов, если пользователь не авторизован

29 сент. 2017 г., 20:54:38
Просмотры: 13.6K
Голосов: 6

Работаю над сайтом технической поддержки на WordPress, где контент доступен только зарегистрированным пользователям, включая загруженные PDF и ZIP файлы.

Ищу способ предотвратить прямой доступ к этим PDF и ZIP файлам внутри директории wp-content/uploads без использования плагинов.

Изучая старые вопросы, нашел очень близкое решение (но комментарии закрыты): https://wordpress.stackexchange.com/a/37743/18608 — оно обнаруживает прямой доступ к файлу, сохраняет запрошенный файл и проверяет авторизацию пользователя. Если пользователь не авторизован, его перенаправляет на страницу входа WordPress. Если авторизован — загружает файл.

Исходный htaccess:

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

Однако этот скрипт блокирует доступ ко всем файлам внутри wp-content/uploads, включая изображения — то есть если пользователь не авторизован, даже изображения в постах (которые не нужно защищать) скрываются на фронтенде.

Пытаюсь изменить RewriteRule так, чтобы проверялись только PDF или ZIP файлы, и dl-file.php вызывался только если запрашивают PDF или ZIP.

Пробовал следующий вариант, но он возвращает "404-File not found." при доступе к PDF или ZIP, даже если пользователь авторизован. Проверка типа файла вроде работает, но проверка dl-file.php не срабатывает.

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

Как изменить правило, чтобы dl-file.php вызывался только для pdf или zip файлов, но при этом корректно передавал информацию в dl-file.php?

Спасибо, Джонатан

/*
 * dl-file.php
 *
 * Защита загруженных файлов с проверкой авторизации.
 * 
 * @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; Файл не найден.');
}

$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 ); // всегда отправляем этот заголовок
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' );

// Поддержка условных GET-запросов
$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'] );
// Если строка пуста, возвращаем 0. Иначе пытаемся преобразовать в timestamp
$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;

// Создаем timestamp для нашего последнего изменения...
$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;
}

// Если дошли до сюда, просто отдаем файл
readfile( $file );
4
Комментарии

Обратите внимание, что если вы добавите этот комментарий в начало ответа /** Plugin Name: Zip Plugin **/, он станет плагином, но ваш вопрос, судя по всему, не имеет отношения к WordPress, а является чистым вопросом по Apache HTAccess

Tom J Nowell Tom J Nowell
29 сент. 2017 г. 21:45:01

Спасибо - в оригинальном посте, на который я ссылаюсь, содержится файл dl-file.php с проверкой входа / перенаправлением, специфичным для WordPress - именно это у меня не получается заставить работать. Пытаюсь проверить pdf и zip файлы, и при этом чтобы условная проверка входа работала для части с перенаправлением. Я добавил больше деталей для ясности.

d38 d38
29 сент. 2017 г. 22:47:45

Я только что заметил, вы изменили dl-file.php на filecheck.php? Предполагаю, что это один и тот же файл?

MrWhite MrWhite
30 сент. 2017 г. 00:54:45

Спасибо, это так - я изменил это здесь, но оставил таким же для примера. Я пропустил ссылку на это.

d38 d38
30 сент. 2017 г. 01:16:15
Все ответы на вопрос 1
6
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/([^/]*\.(pdf|zip))$ filecheck.php?file=$1 [QSA,L]

Этот код выглядит корректным, за исключением случая, когда у вас есть дополнительные подкаталоги внутри директории /uploads. Альтернативой может быть добавление дополнительного условия в исходное правило, которое будет выполнять перезапись только если запрос заканчивается на .pdf или .zip. Например:

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

Это не должно иметь принципиального значения, но убедитесь, что данное правило находится перед фронт-контроллером WordPress.

30 сент. 2017 г. 00:26:22
Комментарии

Спасибо - это имеет смысл. Я тестирую здесь, но по какой-то причине условие входа не срабатывает, поэтому PDF загружается при выходе из системы?

d38 d38
30 сент. 2017 г. 01:18:28

Спасибо, MrWhite - это было правильное направление. У меня не получилось заставить REQUEST_URL работать в первом RewriteCond, но замена на RewriteCond %{REQUEST_FILENAME} .(pdf|zip)$ сработала.

d38 d38
30 сент. 2017 г. 02:32:49

Ой, извините, это была опечатка, должно быть REQUEST_URI, а не REQUEST_URL! (Я обновил свой ответ.) Рад, что у вас заработало!

MrWhite MrWhite
30 сент. 2017 г. 02:45:39

Привет, Спасибо за твой совет. У меня вопрос: если я не авторизован, то при вводе URL с PDF-файлом в браузере меня перенаправляет на страницу входа. Можно ли перенаправлять на страницу 404? Спасибо еще раз.

Samuel Samuel
26 апр. 2019 г. 12:01:05

@sampaii Тебе нужно сделать это в твоем скрипте загрузки/аутентификации (на PHP)... если пользователь не авторизован, то возвращать 404 вместо перенаправления — что, вероятно, сейчас инициируется твоим скриптом, если ты следовал примеру выше?

MrWhite MrWhite
26 апр. 2019 г. 12:59:32

mrwhite, извини, только сейчас увидел твое сообщение. Это проблема с данным методом. Когда я авторизован в бэкенде и ввожу URL любого PDF-файла, меня перенаправляет на страницу 404. Есть идеи, пожалуйста?

Samuel Samuel
12 мая 2020 г. 16:22:28
Показать остальные 1 комментариев