PHP Bilder nur für internen Bereich

Sparta8

Lieutenant
Registriert
Juli 2008
Beiträge
977
Hallo,

ich hab hier eine PHP Webseite/App mit einem internen Bereich. Dort können User Bilder hochladen, die für Gäste von außen nicht erreichbar sein sollen.
Ich weiß nicht genau wie ich das umsetzen soll. Ein kurzer Test über PHP zeigt doch deutlich um wie viel langsamer ein Request abläuft wenn zuerst über PHP/MySQL geprüft werden muss ob der User überhaupt berechtigt ist und erst dann das Bild ausgeliefert wird. Vor allem auf Übersichtseiten wo gleich 50-100 Bilder geladen werden müssen.

Kennt von euch irgendwer eine Möglichkeit, wie ich PHP umgehen könnte und der Webserver(Apache) selbst entscheiden kann ob ein User berechtigt ist?

Habe ich irgendwo einen Denkfehler?

Nur rein aus Interesse, weiß wer wie riesige Seiten wie Facebook/Instagramm das gelöst haben?

Danke für eure Zeit!
 
Lass den User die Bilder laden und nutze die htaccess um das Bilderverzeichnis zu schützen.

Edith: Natürlich solltest du mit einem Script die 503-Meldung abfangen lassen und eine Nutzerfreundlichere Fehlermeldung einblenden.
 
Sparta8 schrieb:
Ich weiß nicht genau wie ich das umsetzen soll. Ein kurzer Test über PHP zeigt doch deutlich um wie viel langsamer ein Request abläuft wenn zuerst über PHP/MySQL geprüft werden muss ob der User überhaupt berechtigt ist und erst dann das Bild ausgeliefert wird. Vor allem auf Übersichtseiten wo gleich 50-100 Bilder geladen werden müssen.

Habe ich irgendwo einen Denkfehler?

Nur rein aus Interesse, weiß wer wie riesige Seiten wie Facebook/Instagramm das gelöst haben?

Da hast du viele Denkfehler ;)
Wenn Entwicklern etwas unterkommt so jeder einzelne Zugriff einzeln geprüft werden muss, sollte einem eigentlich sofort die Alarmglöckchen angehen. Gerade wenn es nur eine A/B Unterscheidung sein soll. Du solltest dich u.a. mit Sessions beschäftigen und dann anhand dieser Sessions entscheiden ob Nutzer div. Unterseiten aufrufen dürfen. Wenn du es richtig machst muss die Datenbank nur zum Überprüfen des Logins einmal abgefragt werden.
Wegen ein paar hundert Abfragen sollte PHP/MYSQL nicht all zu lahm werden. Mit PHP-FPM, stored Procedures und gescheiten Indizes geht sowas auch noch recht fix.
Wenn du es nicht absicherst, dann sind die Bilder verlinkbar, wenn man einmal den direkten Link dazu hat. Wenn dann noch eine Struktur erkennbar ist, dann kann man gern mal am Zugriffsschutz vorbei crawlen, das solltest du unterbinden

Beispiel:
example.com/pics/foobatz_0093.jpg
Da ist ganz schnbell foobatz_0000.jpg bis foobatz_9999.jpg auch abgegriffen wenn du schlampst. Wenn dann auch noch Directory Listing auf pics/ aktiv ist...


Bei den großen Anbietern: Ganz viel schwarze Magie.
 
  • Gefällt mir
Reaktionen: IT_Nerd
Piktogramm schrieb:
Wenn du es richtig machst muss die Datenbank nur zum Überprüfen des Logins einmal abgefragt werden.
Wie geht das?
Wenn das nächste Bild raussoll, muss man doch wieder schauen, ob der User (noch) eingelogged ist.
Rauszögern könnte man das nur mit einem ablaufendem Zertifikat (aber nur für kurze Zeit, da man sonst auch ausgelogged auf die Bilder zugreifen kann).
 
Beschäftige dich mit Sessions! Wenn du etwas programmierst solltest du immer verstanden haben was du da machst und welche Grenzen das hat. Gerade bei Zugriffsbeschränkungen Login!
Mal ganz abgesehen von ich schreibfaul und sehe es nicht ein längere Texte zu schreiben wenn das www davon voll ist es es eine wirklich gute PHP Doku gibt.



Mir sind noch ein paar Denkfehler aufgefallen:
Wenn du 100 Request auf einen Schlag hast, dass bedeutet dass, dass du 100 Bilder gleichzeitig laden willst. Das ist ungünstig. Da solltest du dazu übergehen, dass die Gallerien max. 10-20 Bilder auf einen Schlag anzeigen mit der Funktion diese durchzublättern. Oder aber bei "endlosen" Gallerien wie bei Instagram da mittels Javascript die Bilder asynchron und nach Bedarf nachzuladen (mit der serverseitigen Begrenzung, dass Clientes immer nur 1-10 Requests parallel laufen lassen dürfen).
Ergänzung ()

Noch ein Hinweis, wer im Web entwickelt sollte sich zwingend mal mit den Developertools der Browser beschäftigen. Da kann man zum Beispiel mal anschauen wie Cbase das macht.
Bitte achtet darauf, wenn ihr dazu etwas postet, dass ihr eure Identifier NICHT veröffentlicht.
 
Zuletzt bearbeitet:
Ich habe mich mit Sessions beschäftigt.
Dass speziell PHP-Sessions auch ohne dedizierte DB auskommen, hättest du einfach erwähnen können ;)
 
Darauf hätte der TE allein kommen sollen -.- Ich bin etwas fanatisch darin Leute dazu zu zwingen die Dokus verstehend zu lesen.
 
Danke für die Denkanstöße.
D.h. das höchste der Gefühle wäre z.B. Redis für die Sessions und PHP selbst kann man nicht umgehen.

Natürlich wäre es schlecht wenn eine Seite 100 Bilder auf einmal ladet, aber ist die Geschwindigkeit nicht die gleiche als wenn 25 User gleichzeitig 4 Bilder laden? Ich meine nur damit man so ungefähr die Performance abschätzen kann.

Weiß nicht wie genau Facebook das macht, aber sobald man die URL vom Bild hat, bekommt man dieses auch. Ganz ohne Session und egal mit welcher IP/Location. Die Rechte im Album sind auf "nur für mich" gesetzt. Also Freunde und Gäste sehen die Bilder nicht, aber wenn sie die URL haben, liefert der Server ohne zu mucken die Bilder aus.

Was meinst du mit, ich soll mir anschauen wie CB das macht, hier gibts doch gar keinen internen Bereich?

Danke!
 
Der Abruf der Bilder noch jedes mal einzeln prüfen ist sinnlos. Wenn ein Nutzer das Bild jemandem unberechtigtem zeigen will, gibts da tausend möglichkeiten an einer Überprüfung vorbei zu kommen. Die URLs dürfen halt nicht vorhersehbar sein, sondern zufällige IDs.

Hast du ein einfaches "darf alle internen Bilder sehen" / "darf keine internen Bilder sehen" oder können die Berechtigungen für jeden registrierten Nutzer ander sein?

Im ersten Fall bräuchtest du mit bspw. JWT weder eine (serverseitige) Session noch Datenbankzugriffe beim Seitenaufruf (für die Authorisierung).

Nachtrag:

Eine weitere Alternative wäre die Nutzung eines Object Storage wie bpsw. S3 (self hosted mit bspw Minio (super einfach) oder Ceph (vergleichweise komplex)). Hier kannst du vorsignierte URLs für ein Objekt erstellen, die ein Ablaufdatum enthalten (was für das Anzeigen auf der Website z.B. bei ein paar Sekunden bleiben kann).
 
Zuletzt bearbeitet:
Sessions sind etwas langsamer als direkt eine Datei von Apache/Nginx auszuliefern, aber es sollte trotzdem nicht so langsam sein das das bei einer moderat großen Anwendung sofort auffällt. Ich würde mir zuerst mal ansehen wo der Flaschenhals ist, also PHP oder Datenbank, und ob da nicht vielleicht viel mehr unnötiges im Hintergrund passiert.

Wenn es die Datenbank ist, wie schon gesagt Sessions in Redis speichern oder cachen.

Die schnellste, aber unsicherste Lösung ist einfach die Bilder zufällig zu benennen. Das ist an sich erstmal sicher genug wenn die Namen wirklich zufällig und lang genug sind, aber URLs können in der User History und an anderen stellen leaken. Und jeder der den Link hat kann dann auf die Bilder zugreifen.

Um Sessions ganz zu umgehen kann man z.B. JWTs verwenden (JSON web tokens), dann kann man kryptographisch signiert die Rechte die ein Benutzer hat in dem Token hinterlegen, und dass dann einfach mitschicken. Dann muss man keine Sessions überprüfen, aber sinvoll ist das erst ab einer erheblichen Größe der Anwendung. JWTs haben auch einige signifikante Nachteile, und ich vermute in diesem Fall das das Überprüfen der Session nicht alleine das Problem ist.
 
Sparta8 schrieb:
Danke für die Denkanstöße.
D.h. das höchste der Gefühle wäre z.B. Redis für die Sessions und PHP selbst kann man nicht umgehen.
Wie kommst du auf Redis?
Du kannst auch ohne PHP arbeiten und deine Logik in Python, C, Brainfuck, etc. pp abbilden. Du hast halte den Thread mit PHP angefangen.


Natürlich wäre es schlecht wenn eine Seite 100 Bilder auf einmal ladet, aber ist die Geschwindigkeit nicht die gleiche als wenn 25 User gleichzeitig 4 Bilder laden? Ich meine nur damit man so ungefähr die Performance abschätzen kann.
Richtig erkennt, du kannst 25 Nutzer gleichzeitig versorgen anstatt einen (der auch noch lange warten muss bis 100 Bilder übertragen und gerendert sind).

Weiß nicht wie genau Facebook das macht, aber sobald man die URL vom Bild hat, bekommt man dieses auch. Ganz ohne Session und egal mit welcher IP/Location. Die Rechte im Album sind auf "nur für mich" gesetzt. Also Freunde und Gäste sehen die Bilder nicht, aber wenn sie die URL haben, liefert der Server ohne zu mucken die Bilder aus.
Wenn man dieses Verhalten nicht wünscht muss man sich drum kümmern.


Was meinst du mit, ich soll mir anschauen wie CB das macht, hier gibts doch gar keinen internen Bereich?
Was denkst du wie so ein Forenlogin funktioniert? Gleiche / Ähnliche Technik
Ergänzung ()

Bagbag schrieb:
Der Abruf der Bilder noch jedes mal einzeln prüfen ist sinnlos. Wenn ein Nutzer das Bild jemandem unberechtigtem zeigen will, gibts da tausend möglichkeiten an einer Überprüfung vorbei zu kommen. Die URLs dürfen halt nicht vorhersehbar sein, sondern zufällige IDs.
Man kann auch jeden Request auf den Server auf eine .php umbiegen, die die Session checkt und erst bei positiven Befund Ressourcen ausliefert. Das zieht etwas Performance und sollte auf sensible Bereiche Beschränkt werden, es geht aber. Da muss man nicht mit Security by Obscurity mit zufälligen IDs anfangen o.O
 
Zuletzt bearbeitet:
Sag das mal Facebook oder Google. Das ist kein security by obscurity, sondern "du bekommst den link nur, wenn du berechtigt bist". Die URL darf halt nicht erratbar sein und das geht einfach mit zufälligen bytes, kodiert mit bspw zbase32.

Wenn du immer noch der Meinung bist, dass das security by obscurity ist, dann musst du mir das bitte genauer erläutern, denn der erhalt der URL ist abgesichert.
 
Dalek schrieb:
Sessions sind etwas langsamer als direkt eine Datei von Apache/Nginx auszuliefern, aber es sollte trotzdem nicht so langsam sein das das bei einer moderat großen Anwendung sofort auffällt.
Ich würde soweit gehen, dass mit gescheiter Optimierung (als Stand der Technik wie sie in den Dokumentationen vom Apache, PHP und div. Lehrbüchern steht) für die meisten Anwendungen überhaupt nicht auffällt. Mein popliges NAS mit einem AMD Kabini rennt ins Bandbreitenlimit von 1GB/s bevor die CPU des Gerätes auch nur annähernd in den Seilen hängt.

Wenn es die Datenbank ist, wie schon gesagt Sessions in Redis speichern oder cachen.
Wenn die Datenbank nicht performt sollte man immer diese optimieren und nicht zusätzliche Logikschichten vorsehen die zusätzliche Ressourcen benötigen. Auch hier zählt, wenn man das kleine 1x1 der Datenbanken anwenden kann man recht große, flexible Anwendungen bauen.

Um Sessions ganz zu umgehen kann man z.B. JWTs verwenden (JSON web tokens), dann kann man kryptographisch signiert die Rechte die ein Benutzer hat in dem Token hinterlegen, und dass dann einfach mitschicken. Dann muss man keine Sessions überprüfen, aber sinvoll ist das erst ab einer erheblichen Größe der Anwendung. JWTs haben auch einige signifikante Nachteile, und ich vermute in diesem Fall das das Überprüfen der Session nicht alleine das Problem ist.
:)
 
Piktogramm schrieb:
Wenn die Datenbank nicht performt sollte man immer diese optimieren und nicht zusätzliche Logikschichten vorsehen die zusätzliche Ressourcen benötigen. Auch hier zählt, wenn man das kleine 1x1 der Datenbanken anwenden kann man recht große, flexible Anwendungen bauen.

Caching hat seine Berechtigung, und ab einer gewissen Größe ist es absolut notwendig. Es bring natürlich zusätzliche Komplexität rein, und wenn man es nicht braucht sollte man es einfach halten. Sessions sind halt so eine triviale Aufgabe die z.B. Redis effizienter ausführen kann als eine Datenbank die wesentlich komplexere Queries verarbeiten kann.

Ich bin ein Fan von PostgreSQL, und wenn es würde ich nur das nehmen und keine weitere Komplexität reinbringen. Aber diese Komplexität hat seine Berechtigung, ab einer gewissen Größe. Ich bezweifle stark das dieser Fall Redis braucht, aber grundsätzlich ist es eine sinvolle Strategie ab irgendeinem Punkt Sessions zu cachen.
 
Piktogramm schrieb:
Wie kommst du auf Redis?
Immer wenn ich an performante Sessions denke komm ich auf Redis.

Ich denke, dass JWT die beste Möglichkeit ist. Hat natürlich auch seine Nachteile, aber ist wie geschaffen zur Abfrage von Ressourcen.
 
Bagbag schrieb:
Sag das mal Facebook oder Google. Das ist kein security by obscurity, sondern "du bekommst den link nur, wenn du berechtigt bist". Die URL darf halt nicht erratbar sein und das geht einfach mit zufälligen bytes, kodiert mit bspw zbase32.

Wenn du immer noch der Meinung bist, dass das security by obscurity ist, dann musst du mir das bitte genauer erläutern, denn der erhalt der URL ist abgesichert.
Das zufällige Benennen von Ressourcen um diese vor unberechtigten Zugriff zu schützen ist Security by Obscurity.

Ansonsten gibt Facebook Ressourcen kryptische IDs, der Hintergrund ist aber nicht da irgendwas zu verschleiern sondern eher IDs für Ressourcenzugriffe zu haben die sich mit deren verteilten Datenbanken gut verarbeiten lassen.

https://scontent-ber1-1.xx.fbcdn.ne...4513330576e0ffdc3c8e8a79d24b7&oe=5D9B43E1

Das Interessante und die Absicherung der URLs erfolgt über die Get Variablen, die an die URI angehangen wurden (alles hinter dem ?). Wobei diese Variablen eine gewisse Dynamik aufweisen.

Merke also:
ID der Ressource -> Keine Sicherheitsfeature
Get Variablen -> Teil der Sicherungsschicht (kann man sich jetzt fetzen, ob Get Variablen als Absicherung taugen)
 
Sparta8 schrieb:
Ich denke, dass JWT die beste Möglichkeit ist. Hat natürlich auch seine Nachteile, aber ist wie geschaffen zur Abfrage von Ressourcen.

Ich würde mir richtig viele Gedanken machen bevor ich zu JWTs greife. Wenn die Anwendung gerade nicht auf 10+ Servern läuft, würde ich es überhaupt nicht in Betracht ziehen. JWTs kann man nicht revoken, d.h. wenn man jemand den Zugang wieder entziehen will muss man schonmal deutlich mehr Aufwand betreiben. Man kann JWTs unsicher konfigurieren, und sie sind deutlich größer als Session IDs.

Sessions sind ein Problem ab einer gewissen Größe, aber das ist ein Problem das die meisten nicht haben. Und die Alternativen haben sehr deutliche Nachteile, die man kennen muss.
 
  • Gefällt mir
Reaktionen: Kanibal und new Account()
@Dalek
Wir sind und einig, ab einer gewissen Größe muss man sich Gedanken machen und da ist Redis / JWT eine von vielen Möglichkeiten. Die gerade von dir genannten >10 Maschinen halte ich auch für real.
 
@Piktogramm

Auch wenn ich dir recht gebe, dass es nicht die sicherste Lösung (in Bezug auf den Kram drum herum wie Browserhistorie oder das Teilen der URL) ist und Facebook das tatsächlich nicht (mehr?) so einfach nutzt, ist es trotzdem kein security through obscurity.
Ich kann dir die gesamte Implementierung dazu geben und trotzdem kommst du nicht an die Bilder, weil randomness und nicht obscurity (Unklarheit [wie das System funktioniert]) die Sicherheit darstellt.

Und das Beispiel Facebook passt trotzdem, weil du mit der URL alleine an das Bild kommst. Das entscheidende ist, dass du die URL nur ausgehändigt bekommst, wenn du dir das Bild anschauen darfst und du die URLs nicht erraten kannst.
Das dürfte das selbe Spiel wie bei S3 (mein Vorschlag oben) sein. Einfach eine presigned URL, mit der man zusätzlich die Gültigkeitsdauer der URL beschränken kann.

https://security.stackexchange.com/...ret-guid-in-an-url-security-through-obscurity

https://www.schneier.com/blog/archives/2015/07/googles_unguess.html
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: new Account()
Zurück
Oben