Apache: Beschränken Sie die Bereitstellung von Bildern auf authentifizierte Benutzer

Apache: Beschränken Sie die Bereitstellung von Bildern auf authentifizierte Benutzer

Ich versuche, einen Weg zu finden, den Zugriff auf einen Medienordner in meiner Apache-Konfiguration einzuschränken. Der Ordner nimmt Uploads von einer Django-Site entgegen und Bild-/PDF-Uploads werden authentifizierten Benutzern auf der Site angezeigt. Das Problem ist, dass jeder nicht authentifizierte Benutzer dorthin navigieren kann mysite.com/media/images/pic1.jpg. Das sollte nicht möglich sein; ich habe ein paar Dinge versucht, um dieses Verhalten einzuschränken, aber ich glaube, ich brauche ein oder zwei Hinweise.

erster Versuch: XSendfile

Xsendfile schien zu funktionieren, aber es sendet (wie der Name schon sagt) die Datei zum Download, dann wird meine Seite, die Bilder anzeigen soll, nicht geladen. Es scheint also nicht das zu sein, was ich für meinen Anwendungsfall brauche.

zweiter Versuch: Regel neu schreiben

Ich habe der Apache-Konfiguration einige Umschreibregeln hinzugefügt:

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

Alle Teile der Site, die eine Authentifizierung erfordern, liegen hinter dem /priv/Pfad. Meine Idee war also, dass, wenn dies funktioniert, die Navigation dorthin /media/images/pic1.jpgneu geschrieben werden müsste. Aber auch das hat nicht funktioniert, mysite.com/media/images/pic1.jpgdas Bild wird immer noch angezeigt.

dritter Versuch: Umwelt

Ich habe etwas Ähnliches mit einer Umgebung innerhalb des virtuellen Hosts versucht:

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

Aber auch das hat nicht funktioniert; ich kann immer noch direkt zum Bild navigieren.

vierter Versuch: Gültiger Benutzer erforderlich

Ich habe es Require valid-userzum V-Host hinzugefügt, aber ich kann nicht herausfinden, wie ich es mit dem Django-Benutzermodell vergleichen kann. Nach dieser Änderung würde ich jedes Mal, wenn ich eine Seite lade, die Bilder anzeigt, eine Aufforderung zur Anmeldung erhalten (aber ohne htaccess usw. gibt es keine Authentifizierung und es werden keine Bilder auf der Site angezeigt).

Ich habe dann versucht, das umzusetzen, was hier beschrieben wird (https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/apache-auth/), aber mein Django-Projekt mag es nicht WSGIHandler(im Gegensatz zum Standard get_wsgi_application()). Ich bekomme eine raise AppRegistryNotReady("Apps aren't loaded yet.")Fehlermeldung. Es scheint, als wäre das der vernünftigste Ansatz, aber ich weiß nicht, wie ich das zum WSGIHandlerLaufen bekomme oder wie der Ansatz mit dem funktioniert get_wsgi_application().

Mir ist bewusst, dass ich den Dateien einen schwer zu erratenden UUID-ähnlichen Namen geben könnte, aber das scheint mir eine halbherzige Lösung zu sein. Was ist also meine beste Strategie, um den Zugriff auf den Medienordner einzuschränken, sodass diese Bilder nur innerhalb des Teils der Site verlinkt werden, in dem Benutzer authentifiziert sind?

Ubuntu 20.04, Apache 2.4

| Bearbeiten, einigen Ratschlägen folgen |

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-Protokolle zeigen, dass dieses Skript geladen ist, aber die Funktion wird anscheinend nicht ausgeführt, da die Druckanweisung nicht in den Protokollen auftaucht. Ich habe eine vereinzelte Druckanweisung in diese Datei und in die Datei wsgi.py eingefügt, um sicherzustellen, dass diese Strategie in die Protokolle gelangt. Nur das, was in der Datei wsgi.py enthalten war, gelangte in das Protokoll.

virtueller Host:

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

|eine weitere Bearbeitung |

Ich habe die Antwort akzeptiert, weil jetzt alles funktioniert. Es gab viele bewegliche Teile, die das anfängliche Problem mit der Antwort verursachten. (1) Die Testfunktion check_password wurde nicht in den Apache-Protokollen angezeigt ... nun, sie tauchte /var/log/apache2/error.loganstelle der eingerichteten benutzerdefinierten Protokolle auf. Ich bin mir nicht sicher, warum, aber ok ...

(2) Mein Venv wurde nicht richtig aktiviert und ich habe das nicht wirklich bemerkt, da Django auch auf dem System Python installiert ist. Ich habe das activate_this.pySkript aus einer virtuellen Umgebung kopiert und es zu meinem Venv hinzugefügt und so etwas zu meiner WSGI-Datei hinzugefügt

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

Wenn diese Dinge behoben sind, funktioniert die Funktion check_password, wenn sie aus der Datei wsgi.py aufgerufen wird. „funktioniert“ bedeutet hier, dass der Zugriff auf den Ordner eingeschränkt wird, auf den nicht autorisierte Benutzer keinen Zugriff haben sollten. Benutzer müssen ihre Anmeldeinformationen immer noch zweimal eingeben – einmal in der normalen Django-Ansicht und einmal in der Browser-Eingabeaufforderung. Das ist ärgerlich, aber eigentlich bezog sich meine Frage auf die Zugriffsbeschränkung, also lasse ich sie für einen anderen Tag.

Der Vorschlag in der Antwort, check_password von auth.py aus aufzurufen, funktioniert bei meinem Projekt nicht. Ich erhalte Fehlermeldungen, die darauf schließen lassen, dass es vor wsgi.py aufgerufen wird – es scheint, als wäre das venv nicht geladen oder die Einstellungen wären zum Zeitpunkt des Aufrufs von check_password nicht geladen.

Antwort1

Folgendes macht get_wsgi_application:

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

Es richtet die Django-Umgebung ein, bevor der Handler zurückgegeben wird.

Folgendes sollte in Ihrem wsgi.py funktionieren:

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

Tatsächlich liegt das Problem in der ersten Zeile in modwsgi.py:

UserModel = auth.get_user_model()

weil get_user_model() nach apps_ready sucht und das alles in dem Moment erledigt wird, in dem Python den Dateiimport ausführt!

der bessere Weg wäre, ein separates auth.py zu erstellen und zuerst mit einem einfachen Ausdruck zu prüfen, ob es wirklich von Apache aufgerufen wird, was in Apaches error.log landet:

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

Sobald dies ausgeführt wird, können Sie es durch die Importanweisung ersetzen und djangos check_password() verwenden.

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

Dann so etwas wie das Folgende in 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>

verwandte Informationen