Mysql
 sql >> Datenbank >  >> RDS >> Mysql

php, wie man eine Datei vom Dateiserver mit diesen Informationen aus der Datenbank verknüpft

Ich dachte mir, ich würde eine kurze (für mich ist das kurz) "Antwort" schreiben, nur damit ich meine Punkte zusammenfassen könnte.

Einige "Best Practices" beim Erstellen eines Dateispeichersystems. Dateispeicherung ist eine breite Kategorie, daher kann Ihre Laufleistung für einige davon variieren. Nehmen Sie sie einfach als Vorschlag dessen, was meiner Meinung nach gut funktioniert.

Dateinamen Speichern Sie die Datei nicht unter dem Namen, den ihr der Endbenutzer gegeben hat. Sie können und werden alle möglichen beschissenen Charaktere benutzen, die dir das Leben schwer machen. Einige können so schlimm sein wie ' einfache Anführungszeichen, die es unter Linux im Grunde unmöglich machen, die Datei zu lesen oder sogar (direkt) zu löschen. Manche Dinge können einfach erscheinen wie ein Leerzeichen, aber abhängig davon, wo Sie es verwenden und das Betriebssystem auf Ihrem Server, könnten Sie mit one%20two.txt enden oder one+two.txt oder one two.txt was zu allen möglichen Problemen in Ihren Links führen kann oder auch nicht.

Am besten erstellen Sie einen Hash, so etwas wie sha1 das kann so einfach sein wie {user_id}{orgianl_name} Der Benutzername verringert die Wahrscheinlichkeit von Kollisionen mit Dateinamen anderer Benutzer.

Ich mache lieber file_hash('sha1', $contents) Auf diese Weise können Sie das abfangen, wenn jemand dieselbe Datei mehrmals hochlädt (der Inhalt ist derselbe, der Hash ist derselbe). Wenn Sie jedoch erwarten, große Dateien zu haben, sollten Sie Benchmarking durchführen, um zu sehen, welche Art von Leistung sie hat. Ich handhabe meistens kleine Dateien, also funktioniert das gut. - Beachten Sie - dass die Datei mit dem Zeitstempel immer noch gespeichert werden kann, weil der vollständige Name anders ist, aber es macht es ziemlich einfach zu sehen, und es kann in der Datenbank verifiziert werden.

Unabhängig davon, was Sie tun, würde ich ihm einen Zeitstempel time().'-'.$filename voranstellen . Dies ist eine nützliche Information, da es sich um die absolute Zeit handelt, zu der die Datei erstellt wurde.

Wie für den Namen, den ein Benutzer der Datei gibt. Speichern Sie das einfach im Datenbankeintrag. Auf diese Weise können Sie ihnen den erwarteten Namen zeigen, aber verwenden Sie einen Namen, von dem Sie wissen, dass er immer sicher für Links ist.

$filename ='etwas beschissenes^ fileane.jpg';

$ext = strrchr($filename, '.');

echo "\nExt: {$ext}\n";

$hash = sha1('some crapy^ fileane.jpg');

echo "Hash: {$hash}\n";

$time = time();

echo "Timestamp: {$time}\n";

$hashname = $time.'-'.$hash.$ext;

echo "Hashname: $hashname\n";

Ausgänge

Ext: .jpg
Hash: bb9d2c2c7c73bb8248537a701870e35742b41c02
Timestamp: 1511853063
Hashname: 1511853063-bb9d2c2c7c73bb8248537a701870e35742b41c02.jpg

Sie können es hier ausprobieren

Pfade Speichern Sie niemals den vollständigen Pfad zur Datei. Alles, was Sie in der Datenbank benötigen, ist der Hash aus der Erstellung des Hash-Namens. Der "Root"-Pfad zu dem Ordner, in dem die Datei gespeichert ist, sollte in PHP erfolgen. Dies hat mehrere Vorteile.

  • verhindert Verzeichnisübertragung. Da Sie keinen Teil des Weges umrunden, müssen Sie sich nicht so viele Sorgen machen, dass jemandem ein \..\.. zurutscht dort drin und gehen Orte, die sie nicht sollten. Ein schlechtes Beispiel hierfür wäre jemand, der ein .htpassword überschreibt Datei, indem Sie eine Datei namens that mit dem darin enthaltenen Verzeichnis transversal hochladen.
  • Hat einheitlicher aussehende Links, einheitliche Größe, einheitlichen Zeichensatz.

https://en.wikipedia.org/wiki/Directory_traversal_attack

  • Wartung. Pfade ändern sich, Server ändern sich. Anforderungen an Ihr System ändern sich. Wenn Sie diese Dateien verschieben müssen, aber den absoluten vollständigen Pfad zu ihnen in der DB gespeichert haben, kleben Sie alles mit symlinks zusammen oder alle Ihre Datensätze aktualisieren.

Hiervon gibt es einige Ausnahmen. Wenn Sie sie in einem monatlichen Ordner oder nach Benutzernamen speichern möchten. Sie können diesen Teil des Pfads in einem separaten Feld speichern. Aber selbst in diesem Fall könnten Sie es basierend auf den im Datensatz gespeicherten Daten dynamisch erstellen. Ich habe festgestellt, dass es am besten ist, so wenig Pfadinformationen wie möglich zu speichern. Und sie erstellen eine Konfiguration oder eine Konstante, die Sie an allen Stellen verwenden können, an denen Sie den Pfad zur Datei angeben müssen.

Auch der path und den link sind sehr unterschiedlich, so dass Sie, indem Sie nur den Namen speichern, ihn von jeder gewünschten PHP-Seite aus verlinken können, ohne Daten vom Pfad abziehen zu müssen. Ich fand es immer einfacher, zum Dateinamen hinzuzufügen, als von einem Pfad abzuziehen.

Datenbank (Nur einige Vorschläge, Verwendung kann variieren) Wie immer bei Daten fragen Sie sich, wer, was, wo, wann

  • id - int Automatisches Inkrement des Primärschlüssels
  • user_id - int Fremdschlüssel, wer hochgeladen
  • Hash - char[40] *sha1*, unique was der Hash
  • Hashname - varchar {timestampl}-{hash}.{ext} wo den Dateinamen auf der Festplatte
  • Dateiname - varchar der ursprüngliche Name, den der Benutzer angegeben hat, damit wir ihm den erwarteten Namen zeigen können ( falls das wichtig ist )
  • Status - enum[public,private,deleted,pending.. etc] Status der Datei, je nach Anwendungsfall müssen Sie die Dateien möglicherweise überprüfen, oder einige sind privat, nur der Benutzer kann sie sehen, andere sind möglicherweise öffentlich usw.
  • Statusdatum - timestamp|datetime wann der Status geändert wurde.
  • create_date - timestamp|datetime wann Wenn die Datei erstellt wurde, wird ein Zeitstempel bevorzugt, da er einige Dinge vereinfacht, aber in diesem Fall sollte derselbe Zeitstempel im Hashnamen verwendet werden.
  • tippen - varchar - MIME-Typ, kann nützlich sein, um den MIME-Typ beim Herunterladen usw. einzustellen.

Wenn Sie davon ausgehen, dass verschiedene Benutzer dieselbe Datei hochladen, und Sie den file_hash verwenden Sie können den hash erstellen Feld einen kombinierten eindeutigen Index der user_id und der hash Auf diese Weise würde es nur zu Konflikten kommen, wenn derselbe Benutzer dieselbe Datei hochgeladen hat. Sie können dies auch basierend auf dem Zeitstempel und dem Hash tun, je nach Ihren Anforderungen.

Das sind die grundlegenden Dinge, an die ich denken könnte, dies ist nicht absolut, nur einige Felder, von denen ich dachte, dass sie nützlich wären.

Es ist nützlich, den Hash allein zu haben, wenn Sie ihn selbst speichern, können Sie ihn in einem CHAR(40) speichern für sha1 (nimmt weniger Platz in der DB ein als VARCHAR ) und setzen Sie die Sortierung auf UTF8_bin was binär ist. Dadurch wird bei der Suche darauf zwischen Groß- und Kleinschreibung unterschieden. Obwohl die Wahrscheinlichkeit einer Hash-Kollision gering ist, fügt dies nur ein wenig mehr Schutz hinzu, da Hashes aus Groß- und Kleinbuchstaben bestehen.

Sie können den hashname immer erstellen on the fly, wenn Sie die Erweiterung und den Zeitstempel getrennt speichern. Wenn Sie immer wieder Dinge erstellen, möchten Sie diese vielleicht einfach in der DB speichern, um die Arbeit in PHP zu vereinfachen.

Ich mag es, einfach den Hash in den Link zu setzen, keine Erweiterung, nichts, also sehen meine Links so aus.

http://www.example.com/download/ad87109bfff0765f4dd8cf4943b04d16a4070fea

Echt einfach, echt generisch, sicher in URLs immer gleich groß etc..

Der hashname für diese "Datei" würde so aussehen

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea.jpg

Wenn Sie Konflikte mit derselben Datei und einem anderen Benutzer haben (was ich oben erwähnt habe). Sie können dem Link jederzeit den Zeitstempelteil, die user_id oder beides hinzufügen. Wenn Sie die user_id verwenden, kann es nützlich sein, sie links mit Nullen aufzufüllen. Einige Benutzer können beispielsweise ID:1 haben und einige können ID:234 sein Sie könnten es also auf 4 Stellen auffüllen und zu 0001 machen und 0234 . Dann fügen Sie das dem Hash hinzu, was fast unbemerkt ist:

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea0234.jpg

Das Wichtigste hier ist, dass sha1 ist immer 40 und die ID ist immer 4 wir können die beiden genau und einfach trennen. Und auf diese Weise können Sie es immer noch eindeutig nachschlagen. Es gibt viele verschiedene Optionen, aber so viel hängt von Ihren Bedürfnissen ab.

Zugriff Wie zum Beispiel das Herunterladen. Sie sollten die Datei immer mit PHP ausgeben, geben Sie ihnen keinen direkten Zugriff auf die Datei. Am besten speichern Sie die Dateien außerhalb des Webroots (über dem public_html , oder www Mappe ). Dann können Sie in PHP die Header auf den richtigen Typ setzen und die Datei im Grunde auslesen. Dies funktioniert für so ziemlich alles außer Video. Ich handhabe keine Videos, also ist das ein Thema außerhalb meiner Erfahrung. Aber ich denke, es ist am besten, sich vorzustellen, dass alle Dateidaten Text sind, es sind die Kopfzeilen, die diesen Text in ein Bild oder eine Excel-Datei oder ein PDF verwandeln.

Der große Vorteil, ihnen keinen direkten Zugriff auf die Datei zu gewähren, besteht darin, dass Sie, wenn Sie eine Mitgliederseite haben und nicht möchten, dass Ihre Inhalte ohne Anmeldung zugänglich sind, einfach in PHP überprüfen können, ob sie angemeldet sind, bevor Sie ihnen den Inhalt geben. Und da sich die Datei außerhalb des Webroots befindet, können sie nicht auf andere Weise darauf zugreifen.

Das Wichtigste ist, etwas Konsistentes auszuwählen, das dennoch flexibel genug ist, um alle Ihre Anforderungen zu erfüllen.

Ich bin mir sicher, dass mir noch mehr einfallen wird, aber wenn Sie irgendwelche Vorschläge haben, zögern Sie nicht zu kommentieren.

BASISPROZESSABLAUF

  1. Benutzer übermittelt Formular (enctype="multipart/form-data" )

https://www.w3schools.com/tags/att_form_enctype.asp

  1. Der Server empfängt die Post aus dem Formular Super Globals $_POST und die $_FILES

http://php.net/manual/en/reserved.variables.files .php

$_FILES = [
 'fieldname' => [
        'name' => "MyFile.txt" // (comes from the browser, so treat as tainted)
        'type' => "text/plain" //  (not sure where it gets this from - assume the browser, so treat as tainted)
        'tmp_name' => "/tmp/php/php1h4j1o" // (could be anywhere on your system, depending on your config settings, but the user has no control, so this isn't tainted)
        'error' => "0" //UPLOAD_ERR_OK  (= 0)
        'size' => "123" //   (the size in bytes)
    ]
 ];
  1. Suchen Sie nach Fehlern if(!$_FILES['fielname']['error'])

  2. Anzeigenamen bereinigen $filename = htmlentities($str, ENT_NOQUOTES, "UTF-8");

  3. Datei speichern, DB-Record erstellen ( PSUDO-CODE )

So:

 $path = __DIR__.'/uploads/'; //for exmaple

$time = time();
$hash = hash_file('sha1',$_FILES['fielname']['tmp_name']);
$type = $_FILES['fielname']['type'];
$hashname = $time.'-'.$hash.strrchr($_FILES['fielname']['name'], '.');
$status = 'pending';

if(!move_uploaded_file ($_FILES['fielname']['tmp_name'], $path.$hashname  )){
     //failed
     //do somehing for errors.
     die();
}


//store record in db

http://php.net/manual/en/function.move -hochgeladene-Datei.php

  1. Erstellen Sie einen Link (variiert je nach Routing), der einfache Weg ist, Ihren Link so zu erstellen http://www.example.com/download?file={$hash} aber es ist hässlicher als http://www.example.com/download/{$hash}

  2. Benutzer klickt auf den Link, um zur Download-Seite zu gelangen.

Holen Sie sich INPUT und suchen Sie den Datensatz

$hash = $_GET['file'];

$stmt = $PDO->prepare("SELECT * FROM attachments WHERE hash = :hash LIMIT 1");  
$stmt->execute([":hash" => $hash]);

$row = $stmt->fetch(PDO::FETCH_ASSOC);

print_r($row);

http://php.net/manual/en/intro.pdo.php

usw....

Prost!