Apache: restringe la publicación de imágenes a usuarios autenticados

Apache: restringe la publicación de imágenes a usuarios autenticados

Estoy intentando encontrar una manera de restringir el acceso a una carpeta multimedia en mi configuración de Apache. La carpeta toma cargas desde un sitio de Django y las cargas de imágenes/pdf se muestran en el sitio a los usuarios autenticados. El problema es que cualquier idiota no autenticado puede navegar a mysite.com/media/images/pic1.jpg. Esto no debería ser posible; He intentado algunas cosas para restringir este comportamiento, pero creo que necesito uno o dos consejos.

primer intento: XSendfile

Xsendfile pareció funcionar, pero (como sugiere el nombre) envía el archivo para descargar, luego mi página que se supone que debe mostrar imágenes no se carga. Entonces parece que esto no es lo que necesito para mi caso de uso.

segundo intento: reescribir la regla

Agregué algunas reglas de reescritura a la configuración de Apache:

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

Todas las partes del sitio que requieren autenticación están detrás de la /priv/ruta, por lo que mi idea era que si esto funciona, /media/images/pic1.jpgse reescribiría la navegación. Pero esto tampoco funcionó. mysite.com/media/images/pic1.jpgTodavía se muestra la imagen.

tercer intento: medio ambiente

Probé algo similar con un entorno dentro del virtualhost:

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

Pero esto tampoco funcionó; Todavía puedo navegar directamente a la imagen.

cuarto intento: Requerir usuario válido

Agregué Require valid-useral v-host, pero no sé cómo compararlo con el modelo de usuario de Django. Esto, después de este cambio, recibiría un mensaje para iniciar sesión cada vez que cargara una página que muestra imágenes (pero sin htaccess, etc., no hay nada contra qué autenticar y no se muestran imágenes en el sitio.

Luego intenté implementar lo que se describe aquí (https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/apache-auth/), pero a mi proyecto Django no le gusta WSGIHandler(a diferencia del valor predeterminado get_wsgi_application()). Recibo un raise AppRegistryNotReady("Apps aren't loaded yet.")error. Parece que este podría ser el enfoque más razonable, pero no sé cómo hacer que WSGIHandlerfuncione o el enfoque funciona con get_wsgi_application().

Soy consciente de que podría darle a los archivos un nombre similar a un uuid difícil de adivinar, pero parece una solución a medias. Entonces, ¿cuál es mi mejor estrategia para restringir el acceso a la carpeta multimedia para que estas imágenes solo estén vinculadas dentro de la parte del sitio donde los usuarios están autenticados?

Ubuntu 20.04, Apache 2.4

| Editar, siguiendo algunos consejos |

autenticación.py

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

    return True

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

Los registros de Apache muestran que este script está cargado, pero la función aparentemente no se ejecuta ya que la declaración de impresión no aparece en los registros. Puse una declaración de impresión perdida en este archivo y en el archivo wsgi.py para asegurarme de que esta estrategia llegue a los registros, solo lo que estaba en el archivo wsgi.py llegó al registro.

servidor virtual:

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

|otra edición |

Acepté la respuesta porque ahora todo está funcional. Había muchas piezas móviles, lo que provocó el problema inicial con la respuesta. (1) La función test check_password no aparecía en los registros de Apache... bueno, aparecía en /var/log/apache2/error.loglugar de los registros personalizados que se configuraron. No estoy seguro de por qué, pero está bien...

(2) Mi venv no se activó correctamente y en realidad no me di cuenta porque django también está instalado en el sistema Python. Copié el activate_this.pyscript de un virtualenv y lo agregué a mi venv y agregué algo como esto a mi archivo wsgi

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

Con esas cosas solucionadas, la función check_password funciona cuando se llama desde el archivo wsgi.py. "funciona" aquí significa que restringe el acceso a la carpeta a la que los usuarios no autorizados no deberían tener acceso. Los usuarios aún deben proporcionar credenciales dos veces: una en la vista normal de Django y otra en el indicador del navegador. Esto es irritante, pero en realidad mi pregunta era sobre restringir el acceso, así que lo dejaré para otro día.

La sugerencia de la respuesta de llamar a check_password desde auth.py no coopera con mi proyecto. Recibo errores que sugieren que se llama antes de wsgi.py; parece que el venv no está cargado o la configuración no está cargada en el momento en que se llama a check_password.

Respuesta1

esto es lo que está haciendo get_wsgi_application:

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

está configurando el entorno django antes de devolver el controlador.

Lo siguiente debería funcionar en su wsgi.py:

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

De hecho, el problema es la primera línea de modwsgi.py:

UserModel = auth.get_user_model()

¡Porque get_user_model() verificará apps_ready y todo eso se hace en el momento en que Python ejecuta la importación del archivo!

la mejor manera sería crear un auth.py separado y primero verificar si Apache realmente lo llama con una impresión simple que irá al error.log de Apache:

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

Una vez que esto se esté ejecutando, puede reemplazarlo con la declaración de importación y usar djangos check_password().

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

Luego algo como lo siguiente en 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>

información relacionada