
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.jpg
se reescribiría la navegación. Pero esto tampoco funcionó. mysite.com/media/images/pic1.jpg
Todaví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-user
al 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 WSGIHandler
funcione 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.log
lugar 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.py
script 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>