Media Verzeichnis schützen

Django, Flask, Bottle, WSGI, CGI…
knasan
User
Beiträge: 11
Registriert: Montag 26. Januar 2009, 18:36

Hallo Forum,

ich habe nachdem ich etliche Tutorials durch habe mein erstes Django Projekt gestartet. Bisher läuft es gut.
Was ich brächte und konnte bisher keine Lösung finden, ein Ordner mit Medien (Podcast, Videos, Bilder usw.) und man darf nur auf diese Dateien zugreifen wenn sich jemand angemeldet hat.
Also wenn jemand die URL kennt, sollte eine Fehlermeldung kommen, nicht Autorisiert.

Static files ist ja nicht dafür geeignet.

Dateien in ein Verzeichnis Hochladen, funktioniert super. Aber wenn ich es mir anzeigen möchte wird der Zugriff verweigert. Definiere ich es als Static - kann jeder zugreifen was ich aber nicht möchte. Kennt jmd. eine Möglichkeit wie man das umsetzten könnte? Tutorial oder ähnliches?

Danke.

Gruß
Sandy
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Handel das ganze über einen View ab, für den man einen entsprechenden Login braucht.
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

Im Produktionsbetrieb werden alle statischen Dateien doch durch einen vorgeschalteten Webserver (nginx, ...) ausgeliefert. Insofern muss du das gewünschte Verhalten für /media nach meinem Verständnis dort konfigurieren.
__deets__
User
Beiträge: 14533
Registriert: Mittwoch 14. Oktober 2015, 14:29

@nezzcarth: das geht nicht. Es ist eben nicht statisch. Sondern dynamisch. Dannmuss da Code laufen. Entsprechen ist alles was man tun muss eine Route anlegen, die für den Rest des Pfades (oder anderer Parameter) prüft, ob wer eingeloggt ist, und ob derjenige das Recht hat, eine bestimmte Resource zu sehen.
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

__deets__ hat geschrieben: Dienstag 29. Oktober 2019, 23:31 @nezzcarth: das geht nicht. Es ist eben nicht statisch. Sondern dynamisch. Dannmuss da Code laufen.
Aber media files wie die angesprochenen Podcasts sind doch statische Dateien? Wie werden die denn dann ausgeliefert? Ich verstehe schon, dass django da mit Routen regulieren kann, etc.. Aber letztendlich liegen die Dateien doch in der Hand des Webservers und sind direkt ansteuerbar, wenn man den Pfad kennt, oder nicht? :o :?:
__deets__
User
Beiträge: 14533
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn sie das wären, wären sie ungeschützt. Und du hättest ein Problem. Das einzige das man da verbessern könnte - theoretisch, weiß nicht, ob das wirklich wer tut - wäre aus Python (oder was auch immer dynamisch rennt) eine filedeskriptor zu übergeben an den nginx zb. Damit müsste man die Blöcke von Daten nicht erst durch einen und dann den anderen Prozess in den netzwerkstack übergeben. Ob das tatsächlich gemacht wird weiß ich nicht, eine kurze Recherche ergab dazu erstmal nichts definitives.
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

Vielleicht bringe ich etwas durcheinander, aber ich beziehe mich auf das, was in der django-Doku zum Beispiel hier angesprochen wird: https://docs.djangoproject.com/en/2.2/h ... eployment/ Nach meinem Verständnis ist django im Produktivbetrieb nicht dafür gedacht, Bilder, Videos, Audiodateien etc. selbst auszuliefen, sondern überlässt dies einem Webserver. In meinen Django-Setups habe ich das über Reverseproxy-Regeln für STATIC_URL, MEDIA_URL, ... gelöst. Wenn ich die Frage korrekt verstanden habe, möchte der Fragensteller einen geschützten Ordner mit solchen Dateien anbieten, die Django nicht selbst auslieferen kann. Und wenn da ein Zugangsschutz drauf sein soll, muss dieser vom Webserver durchgesetzt werden; mit etwas Bastelei kann man den vielleicht sogar an die Django-Authentifizierung koppeln (etwa per https://httpd.apache.org/docs/2.4/mod/m ... n_dbd.html). ...möglicherweise denke ich auch zu umständlich... :)
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@nezzcarth: Wie hast du es denn in deinen Projekten gelöst, dass nur ein Benutzer in Django mit bestimmten Rechten die Datei ausgeliefert bekommt? Denn darum geht es dem Fragesteller ja.
Dass Dateien, die keinem Schutz unterliegen, über einen separaten Webserver ausgeliefert werden können, steht ja völlig außer Frage.

"etwas Bastelei" würde bedeuten, dass man dem Webserver für die statischen Dateien beibringt, die Session-Daten des dynamischen Teils zu verwenden. Denn nur dort existiert die Information, ob der korrekte Benutzer gerade eingeloggt ist und in der entsprechenden Session eine Datei ausgeliefert werden darf.
Dem Webserver für den statischen Teil einfach an die Benutzer/Passwort-Authentifizierung des Webframeworks zu hängen, so dass dort das separate Eingeben der Daten nötig ist, ist gefühlt schon seit 20 Jahren nicht mehr benutzerfreundlich.
Zuletzt geändert von sparrow am Mittwoch 30. Oktober 2019, 09:06, insgesamt 1-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nezzcarth: Was heisst nicht dafür gedacht? Man kann statische Dateien auch über Django ausliefern. Macht man normalerweise nicht, aber wenn man es *muss*, dann muss man es halt. Alternative wäre auf Anforderung in einem über den Webserver ausgelieferten Verzeichnis Links (Dateisystemebene) auf die Dateien anzulegen mit einem nicht erratbaren Namen (UUID oder so) und die dem Benutzer anzubieten. Das verringert die Angriffsfläche, lässt aber eine Lücke.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14533
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Aussage, Django wäre zur Auslieferung statischer Inhalte ungeeignet ist wie so viele Aussagen eine, hinter der ein Komma und dann mehrere Absätze zu stehen haben. Denn es kommt dabei wie immer darauf an, in welchen Szenarien man was macht. Selbstverständlich KANN Django diese Inhalte ausliefern. Das Problem dabei ist nur, das die Overhead-Kosten pro ausgeliefertem Byte deutlich höher sind, als das der Fall ist bei einem auf dieses ausliefern spezialisierten System wie NGINX etc. Man kann also die Auslastung des Systems verringern respektive mehr requests abarbeiten, wenn man Django hinter NGINX (oder Apache, aber ich will die nicht immer beide erwähnen) betreibt. Aber wieso sind die darin besser? Weil sie optimiert sind. Aber was heißt denn das? Sie sind zum einen in weniger CPU und RAM-hungrigen Sprache geschrieben. Vor allem aber haben sie ein deutlich reduziertes Regelsystem, nach dem sie Inhalte anbieten. Und die Frage ist nun: reicht das für den Use-Case? die Antwort ist eben: nein. Basic Auth und andere primitive Verfahren wie sie diese Systeme unterstützen kommen nicht weit genug. Die sagen nur, sie kennen einen User. Und das noch nicht mal großartig dynamisch. Sie sind NICHT in der Lage, die Entscheidung ob dieser User etwas bekommen darf an komplexen Business-Regeln festzumachen. Wie zb der Bezahlung eines Abos bei Netflix. Dementsprechend kann man seine Inhalte nur dann von so einem simplen System ausliefern lassen, wenn dabei keine Gefahr für das Geschäftsmodell besteht.

Die Rechteprüfung hier ist der entscheidende Teil der Logik, und kann nicht durch NGINX erbracht werden. Dass die aus dem bestehen der Prüfung resultierenden Bytes von einer Festplatte kommen, ist für diese Betrachtung irrelevant.

Natürlich kann man bei entsprechend hohem Druck auf die Server andere Wege beschreiten. Dann muss man aber an den Kern von NGINX, und zb eine Zugangsberechtigung via Redis und dort von der Anwendung hinterlegte Berechtigungen programmieren. Selbst. Jemand wie Netflix muss das. Der TE hier? Nein. Und die gewünschte Funktionalität ist mit Bordmitteln eben nur durch das durchschleusen der Daten durch den Django Prozess möglich.

Es gibt ggf noch hybride Lösungen durch zb erzeugen von GUID symlinks auf Inhalte, und redirects. Allerdings wurde auch das zumindest temporär für JEDEN Zugriff erlauben. Man kann den Link nur nicht einfach permanent teilen. Aber auch hier gilt: das ist viel Aufwand, den man nur treiben muss, wenn man sonst irgendwie an Grenzen kommt.
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@nezzcarth: das ist halt kompliziert, die Authentifizierung sowohl in Django als auch im Webserver korrekt durchzuführen.

Ich würde den Weg der internen Weiterleitung gehen: https://clubhouse.io/developer-how-to/h ... -in-nginx/
Die Anfrage landet also über eine Route direkt in Django, das antwortet aber nur mit einem internen Link, die Auslieferung findet durch den Reverse-Proxy statt.
__deets__
User
Beiträge: 14533
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Sirius3: das ist genau das Feature, das ich gesucht aber nicht gefunden habe. Sehr cool.
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

Danke für die ganzen ausführlichen Antworten, das war sehr hilfreich. Dass Django keine statischen Files ausliefern kann/sollte, ist nach meinem Eindruck die gängige Meinung im Django-Umfeld; zumindest habe ich die Docs so interpretiert und Stackoverflow, reddit usw. Antworten, die ich dazu gesehen habe, vermitteln das oft. Aber ihr habt ja ein paar Argumente gebracht, dass man das vielleicht differenzierter sehen muss.

Die von Sirius3 vorgeschlagene Methode mit dem speziellen Header+Redirect habe ich bei der Recherche auch gefunden (über https://github.com/johnsensible/django-sendfile). Allerdings war ich mir unsicher, wie aktuell das ist, da zumindest das Django-Modul sowie das 3rd Party Apache Modul mod_xsendfile angestaubt wirken (letzteres kann auch an Apache liegen).

Selbst hatte ich den Fall noch nicht, dass ich das gebraucht hätte.
knasan
User
Beiträge: 11
Registriert: Montag 26. Januar 2009, 18:36

Danke für eure Antworten. Ich habe auch einige Projekte gefunden die sich mit Django Files Download beschäftigen aber konnte diese noch nicht genau ansehen. Da ich gerade noch an ein paar dinge in mein Projekt hänge (ist auch erst mein erstes Projekt). https://github.com/django-xxx/django-file-download
Ich werde, sobald ich meine anderen Probleme gelöst habe, mich um das Problem mit dem Download kümmern und dann hier berichten.

Nochmal vielen dank für euere Unterstützung.

Gruß Sandy
knasan
User
Beiträge: 11
Registriert: Montag 26. Januar 2009, 18:36

Ich habe mir jetzt mal ein Beispiel Projekte angelegt um das Handling zu lernen.
In meinem MEDIA Ordner habe ich eine Datei,
auf den MEDIA Ordner habe ich einen View drauf gelegt, der alles was nach /media kommt einfach ignoriert.

Test 1.
http://localhost:8000/media -> Super mein View
http://localhost:8000/media/file.jpg -> Super mein View

im Template
<img src="/media/file.jpg" alt="file"> -> kann nicht angezeigt werden, vermutlich weil nun auch mein View drauf schaut. Mein View macht einfach nur ein Redirect auf ein Template ... sonst macht der nichts.

Wie würdet ihr das mit einem View lösen? Dies schien mir erst mal das Sinnvollste zu sein.
NGINX habe ich erstmal nicht, das ich die Seite erstmal Entwerfen muss und ich nutze lediglich manage.py runserver ...

Andere Idee: Die Dateien in einer Datenbank schreiben und festhalten auf welche Dateien der User Zugriff hat, der andere mehr und der andere eben weniger. Die Files also erst gar nicht auf das Filesystem ablegen.
Aber ich habe gelesen das dies mehr Nachteile mit sich bringen würde statt Vorteile. Deswegen habe ich diese Idee erst einmal nicht weiter verfolgt.

Gruß Sandy
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@knasan: Bei der zweiten URL, der mit dem *.jpg verstehe ich das ”Super“ nicht, denn da sollte ja die Datei kommen und nicht Dein View.

Ohne Code kann man da nicht viel sagen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
knasan
User
Beiträge: 11
Registriert: Montag 26. Januar 2009, 18:36

__blackjack__ hat geschrieben: Samstag 2. November 2019, 17:54 @knasan: Bei der zweiten URL, der mit dem *.jpg verstehe ich das ”Super“ nicht, denn da sollte ja die Datei kommen und nicht Dein View.

Ohne Code kann man da nicht viel sagen.
Nein, ich möchte ja versuchen das Verzeichnis (oder irgend ein anderes) vor ungewollten Zugriff schützen. Also wenn jemand die URL der Datei kennt, sollte er nicht die Datei sehen sondern mein View. Das geht auch soweit, aber wenn ich jemanden die Datei ausliefern möchte. K.A wie.

in mein Beispiel habe ich folgendes gemacht.

url.py

Code: Alles auswählen

url(r'^media/', views.listfiles, name='listfiles'),
view.py

Code: Alles auswählen

....
def index(request):
    return render(request, 'home.html')

def listfiles(request):
    return render(request, 'home.html')
settings.py

Code: Alles auswählen

STATIC_URL = '/static/'
#STATIC_ROOT = os.path.join(BASE_DIR, 'static')

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
und Im Template habe ich einfach ein IMG TAG auf die Datei gemacht, Browser sagt vermutlich zurecht IMG kann nicht gefunden werden.

Code: Alles auswählen

<img src="/media/path_to_file">
Gruß Sandy
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

Nein, Du willst ja die Datei ausliefrrn, wenn der Nutzer eingeloggt ist. Nur wenn der Nutzer keine Rechte hat oder nicht eingeloggt ist, denn willst Du eine Fehlerseite ausgeben.
knasan
User
Beiträge: 11
Registriert: Montag 26. Januar 2009, 18:36

@Sirius, ja genau du hast recht.
knasan
User
Beiträge: 11
Registriert: Montag 26. Januar 2009, 18:36

Hi,

ich habe noch etwas rumgespielt und hab dazu auch ein paar Fragen.

1. urls.py

Code: Alles auswählen

re_path(r'^media/\w+/|\w+/\w+$', views.listfiles, name='listfiles'),
2. views.py

Code: Alles auswählen

def listfiles(request):
    username = str(request.user)
    path = os.path.join(settings.MEDIA_ROOT, username)
    allfiles = os.listdir(path)
    filename = request.path.split('/')[-1:][0]
    if filename in allfiles:
        return HttpResponseNotFound('<h1>Page not found</h1>')
    return render(request, 'home.html')
Dies ist nur ein Beispiel Projekt zum rumspielen.
Wenn ich alles verstanden habe könnte man so einigermaßen realisieren.
zu 1. eine route schreiben die erkennt ob man auf das geschützte Verzeichnis zugreifen möchte und ggf. ob eine Datei zum Download angefordert wurde.

Noch nicht soweit geschrieben, hier nur das Theoretische.
Im View zu dieser route (in meinem Beispiel listfiles) prüfen ob request.user enthalten ist, wenn nicht ein 404 oder ähnliches machen.
Eine Datenbank bereitstellen, die den Dateinamen und Username (id mit Fremdschlüssel) welcher Benutzer berechtigt ist. Wenn ein Benutzer Angemeldet ist und versucht eine Datei zu laden ein Query in der Datenbank ob dieser berechtigt ist. Wenn nicht HttpResponseNotFound (oder 404 usw), wenn berechtigt, dann einfach ein Header abschicken der besagt, lade mir die Datei runter.

So wie es hier beschrieben ist? : https://stackabuse.com/download-files-with-python/

1. filename = request.path.split('/')[-1:][0]
So hole ich mir derzeit die Datei aus dem Request, gibt es da eine eleganteren Weg?
2. Nach dem MVC wäre es ja falsch im View logik einzubauen, aber wenn ich so eine logik benötige, wie sollte man diese integrieren?

Gruß Sandy
Antworten