Apache: ограничить показ изображений только для аутентифицированных пользователей

Apache: ограничить показ изображений только для аутентифицированных пользователей

Я пытаюсь найти способ ограничить доступ к папке media в моей конфигурации Apache. Папка принимает загрузки с сайта Django, а загрузки изображений/pdf отображаются на сайте для аутентифицированных пользователей. Проблема в том, что любой неаутентифицированный schmo может перейти в . mysite.com/media/images/pic1.jpgЭто не должно быть возможным; я пробовал несколько вещей, чтобы ограничить это поведение, но я думаю, что мне нужен указатель или два.

первая попытка: XSendfile

Xsendfile, похоже, работает, но он (как следует из названия) отправляет файл на загрузку, а затем моя страница, которая должна отображать изображения, не загружается. Так что, похоже, это не то, что мне нужно для моего варианта использования.

вторая попытка: переписать правило

Я добавил несколько правил перезаписи в конфигурацию Apache:

RewriteCond "%{HTTP_REFERER}" "!^$"
RewriteCond "%{HTTP_REFERER}" "!mysite.com/priv/" [NC]
RewriteRule "\.(gif|jpg|png|pdf)$"    "-"   [F,NC]

Все части сайта, требующие аутентификации, находятся за путем /priv/, поэтому моя идея заключалась в том, что если это сработает, то переход на /media/images/pic1.jpgбудет переписан. Но это тоже не сработало, mysite.com/media/images/pic1.jpgизображение все еще отображается.

третья попытка: окружающая среда

Я попробовал нечто подобное со средой внутри виртуального хоста:

<VirtualHost *:80>
    ...
    SetEnvIf Referer "mysite\.com\/priv" localreferer
    SetEnvIf Referer ^$ localreferer
    <FilesMatch "\.(jpg|png|gif|pdf)$">
        Require env localreferer
    </FilesMatch>
    ...
</VirtualHost>

Но это тоже не сработало; я по-прежнему могу перейти непосредственно к изображению.

четвертая попытка: Требовать действительного пользователя

Я добавил Require valid-userв v-host, но не могу понять, как проверить его на соответствие модели пользователя Django. После этого изменения я получал запрос на вход каждый раз при загрузке страницы, которая отображает изображения (но без htaccess и т. д. нечему авторизоваться, и на сайте не отображаются изображения).

Затем я попытался реализовать то, что описано здесь (https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/apache-auth/), но мой проект django не любит WSGIHandler(в отличие от default get_wsgi_application()). Я получаю raise AppRegistryNotReady("Apps aren't loaded yet.")ошибку. Кажется, это может быть наиболее разумным подходом, но я не знаю, как заставить его WSGIHandlerработать, или как этот подход работает с get_wsgi_application().

Я знаю, что я мог бы дать файлам трудноугадываемое имя типа uuid, но это кажется половинчатым решением. Итак, какова моя лучшая стратегия, чтобы ограничить доступ к папке с медиа, чтобы эти изображения были связаны только в той части сайта, где пользователи аутентифицированы?

Ubuntu 20.04, Apache 2.4

| Редактировать, следуя некоторым советам |

auth.py

def check_password(environ, username, password):
    print("---->>>---->>>---->>>---->>>---->>> check_password() has been called  <<<----<<<----<<<----<<<----<<<----")

    return True

#from django.contrib.auth.handlers.modwsgi import check_password

Логи Apache показывают, что этот скрипт загружен, но функция, по-видимому, не выполняется, поскольку оператор печати не появляется в логах. Я поместил случайный оператор печати в этот файл и в файл wsgi.py, чтобы убедиться, что эта стратегия попадает в логи, в лог попало только то, что было в файле wsgi.py.

vhost:

<VirtualHost *:80>
    ServerName mysite.com
    ServerAlias mysite.com
    DocumentRoot /path/to/docroot/
    
    Alias /static/ /path/to/docroot/static/

    # Not sure if I need this
    Alias /media/ /path/to/docroot/media/

    <Directory /path/to/docroot/static/>
        Require all granted
    </Directory>

    <Directory /path/to/docroot/media/>
        Require all granted
    </Directory>

    # this is my restricted access directory
    <Directory /path/to/docroot/media/priv/>
        AuthType Basic
        AuthName "Top Secret"
        AuthBasicProvider wsgi
        WSGIAuthUserScript /path/to/docroot/mysite/auth.py
        Require valid-user
    </Directory>

    <Directory /path/to/docroot/mysite/>
        <Files "wsgi.py">
            Require all granted
        </Files>
    </Directory>

    WSGIDaemonProcess open-ancestry-web python-home=/path/to/ENV/ python-path=/path/to/docroot/ processes=10 threads=10
    WSGIProcessGroup mysite-pgroup
    WSGIScriptAlias / /path/to/docroot/mysite/wsgi.py

    LogLevel trace8
    ErrorLog "|/bin/rotatelogs -l /path/to/logs/%Y%m%d-%H%M%S_443errors.log 30"
    CustomLog "|/bin/rotatelogs -l /path/to/logs/%Y%m%d-%H%M%S_443access.log 30" combined
</VirtualHost>

|еще одна правка |

Я принял ответ, потому что теперь все работает. Было много подвижных частей, которые и вызвали первоначальную проблему с ответом. (1) Тестовая функция check_password не отображалась в журналах Apache... ну, она отображалась /var/log/apache2/error.logвместо настроенных пользовательских журналов. Не уверен, почему, но ладно...

(2) Мой venv не был активирован должным образом, и я на самом деле не заметил этого, потому что django установлен в системе Python также. Я скопировал скрипт activate_this.pyиз virtualenv и добавил его в свой venv и добавил что-то вроде этого в свой wsgi-файл

activate_this = '/path/to/ENV/bin/activate_this.py'
with open(activate_this) as f:
    exec(f.read(), {'__file__': activate_this})

После исправления этих вещей функция check_password работает при вызове из файла wsgi.py. «Работает» здесь означает, что она ограничивает доступ к папке, к которой неавторизованные пользователи не должны иметь доступа. Пользователям по-прежнему нужно предоставлять учетные данные дважды — один раз в обычном представлении django и один раз в приглашении браузера. Это раздражает, но на самом деле мой вопрос был об ограничении доступа, поэтому я оставлю его на другой день.

Предложение ответа вызвать check_password из auth.py не сотрудничает с моим проектом. Я получаю ошибки, которые предполагают, что он вызывается до wsgi.py – похоже, что venv не загружен или настройки не загружены во время вызова check_password.

решение1

вот что делает get_wsgi_application:

def get_wsgi_application():
    django.setup(set_prefix=False)     # this will lead to "apps_ready=true"
    return WSGIHandler()

он настраивает среду django перед возвратом обработчика.

Следующее должно сработать в вашем wsgi.py:

application = get_wsgi_application()
from django.contrib.auth.handlers.modwsgi import check_password
# the sequence is important!!

На самом деле проблема в первой строке modwsgi.py:

UserModel = auth.get_user_model()

потому что get_user_model() проверит apps_ready и все это будет сделано в тот момент, когда python выполнит импорт файла!

Лучшим способом было бы создать отдельный auth.py и сначала проверить, действительно ли он вызывается Apache, с помощью простого вывода, который будет отправлен в error.log Apache:

def check_password(environ, username, password):
    print("***********   check_password() has been called  ********")
    return True

После запуска вы можете заменить его оператором import и использовать djangos check_password().

from django.contrib.auth.handlers.modwsgi import check_password

Затем в httpd-vhosts.conf что-то вроде следующего:

<VirtualHost *:80>

   ....

   <Directory path_to_server_root/secret>
        AuthType Basic
        AuthName "Top Secret"
        AuthBasicProvider wsgi
        WSGIAuthUserScript path_to_wsgi/wsgi.py
        Require valid-user
   </Directory>

</VirtualHost>

Связанный контент