Mysql
 sql >> Baza danych >  >> RDS >> Mysql

php jak połączyć plik z serwera plików z informacjami z bazy danych

Pomyślałem, że napiszę krótką (dla mnie jest to krótka) „odpowiedź”, aby móc podsumować swoje punkty.

Kilka „najlepszych praktyk” podczas tworzenia systemu przechowywania plików. Przechowywanie plików to szeroka kategoria, więc w przypadku niektórych z nich przebieg może się różnić. Potraktuj je jako sugestię dotyczącą tego, co znalazłem.

Nazwy plików Nie przechowuj pliku pod nazwą nadaną przez użytkownika końcowego. Mogą i będą używać wszelkiego rodzaju gównianych postaci, które uczynią twoje życie nieszczęśliwym. Niektóre mogą być tak złe jak ' pojedyncze cudzysłowy, które w Linuksie w zasadzie uniemożliwiają odczytanie, a nawet usunięcie pliku (bezpośrednio). Niektóre rzeczy mogą wydawać się proste, na przykład spację, ale w zależności od tego, gdzie go używasz i systemu operacyjnego na serwerze, możesz skończyć z one%20two.txt lub one+two.txt lub one two.txt co może, ale nie musi, powodować wszelkiego rodzaju problemy w twoich linkach.

Najlepszą rzeczą do zrobienia jest utworzenie skrótu, takiego jak sha1 może to być tak proste, jak {user_id}{orgianl_name} Nazwa użytkownika zmniejsza prawdopodobieństwo kolizji z nazwami plików innych użytkowników.

Wolę robić file_hash('sha1', $contents) w ten sposób, jeśli ktoś prześle ten sam plik więcej niż raz, możesz go złapać (zawartość jest taka sama, hash jest taki sam). Ale jeśli spodziewasz się dużych plików, możesz wykonać na nim testy porównawcze, aby zobaczyć, jaki rodzaj wydajności ma. Przeważnie zajmuję się małymi plikami, więc działa to dobrze.-zauważ, że ze znacznikiem czasu plik można nadal zapisać, ponieważ pełna nazwa jest inna, ale sprawia, że ​​jest dość łatwy do zobaczenia i można go zweryfikować w bazie danych.

Niezależnie od tego, co zrobisz, poprzedziłbym go znacznikiem czasu time().'-'.$filename . Jest to przydatna informacja, ponieważ jest to bezwzględny czas utworzenia pliku.

Jeśli chodzi o nazwę to użytkownik podaje plik. Po prostu zapisz to w rekordzie bazy danych. W ten sposób możesz pokazać im nazwę, jakiej oczekują, ale użyj nazwy, o której wiesz, że jest zawsze bezpieczne dla linków.

$filename ='trochę gówna^ 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";

Wyjścia

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

Możesz spróbować tutaj

Ścieżki nigdy nie przechowuj pełnej ścieżki do pliku. Wszystko, czego potrzebujesz w bazie danych, to skrót od utworzenia nazwy skrótu. Ścieżka "root" do folderu, w którym jest przechowywany plik, powinna być wykonana w PHP. Ma to kilka zalet.

  • uniemożliwia przenoszenie katalogów. Ponieważ nie omijasz żadnej części ścieżki wokół siebie, nie musisz się tak bardzo martwić, że ktoś poślizgnie się \..\.. tam i w miejscach, do których nie powinni. Kiepskim tego przykładem może być ktoś nadpisujący .htpassword pliku, przesyłając plik o nazwie tak z katalogiem poprzecznym w nim.
  • Ma bardziej jednolicie wyglądające linki, jednolity rozmiar, jednolity zestaw znaków.

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

  • Konserwacja. Zmieniają się ścieżki, zmieniają się serwery. Zmieniają się wymagania dotyczące Twojego systemu. Jeśli potrzebujesz przenieść te pliki, ale zachowałeś do nich absolutną pełną ścieżkę w bazie danych, utknąłeś sklejając wszystko razem za pomocą symlinks lub aktualizować wszystkie swoje rekordy.

Są od tego wyjątki. Jeśli chcesz je przechowywać w miesięcznym folderze lub według nazwy użytkownika. Możesz zapisać tę część ścieżki w osobnym polu. Ale nawet w takim przypadku można by go zbudować dynamicznie na podstawie danych zapisanych w rekordzie. Uważam, że najlepiej jest zapisać jak najmniej informacji o ścieżce. I tworzą konfigurację lub stałą, której możesz użyć we wszystkich miejscach, w których musisz umieścić ścieżkę do pliku.

Również path i link są bardzo różne, więc zapisując tylko nazwę, możesz połączyć ją z dowolną stroną PHP bez konieczności odejmowania danych ze ścieżki. Zawsze uważałem, że łatwiej jest dodać do nazwy pliku niż odjąć od ścieżki.

Baza danych (tylko kilka sugestii, zastosowanie może się różnić )Jak zawsze w przypadku danych zadaj sobie pytanie, kto, co, gdzie, kiedy

  • identyfikator - int automatyczne przyrosty klucza głównego
  • identyfikator_użytkownika - int klucz obcy, kto przesłał go
  • hasz - char[40] *sha1*, unique co hasz
  • hashname - varchar {timestampl}-{hash}.{ext} gdzie nazwa pliku na dysku twardym
  • nazwa pliku - varchar oryginalna nazwa podana przez użytkownika, w ten sposób możemy pokazać mu nazwę, jakiej oczekuje (jeśli to ważne)
  • stan - enum[public,private,deleted,pending.. etc] status pliku, w zależności od przypadku użycia, może być konieczne przejrzenie plików, a może niektóre są prywatne, tylko użytkownik może je zobaczyć, może niektóre są publiczne itp.
  • data_stanu - timestamp|datetime czas zmiany statusu.
  • data_utworzenia - timestamp|datetime kiedy kiedy plik został utworzony, preferowany jest znacznik czasu, ponieważ ułatwia to niektóre rzeczy, ale w takim przypadku powinien to być ten sam znacznik czasu w nazwie skrótu.
  • typ - varchar - typ mimu, może być przydatny do ustawiania typu mimu podczas pobierania itp.

Jeśli oczekujesz, że różni użytkownicy prześlą ten sam plik i użyjesz file_hash możesz zrobić hash pole połączony unikalny indeks user_id i hash w ten sposób konflikt byłby tylko wtedy, gdyby ten sam użytkownik przesłał ten sam plik. Możesz to również zrobić w oparciu o znacznik czasu i hash, w zależności od potrzeb.

To podstawowe rzeczy, o których mogłem pomyśleć, to nie jest absolutne tylko niektóre dziedziny, które moim zdaniem byłyby przydatne.

Przydatne jest posiadanie samego skrótu, jeśli przechowujesz go samodzielnie, możesz go zapisać w CHAR(40) for sha1 (zajmuje mniej miejsca w DB niż VARCHAR ) i ustaw sortowanie na UTF8_bin który jest binarny. To sprawia, że ​​w wyszukiwaniu rozróżniana jest wielkość liter. Chociaż prawdopodobieństwo kolizji skrótów jest niewielkie, zapewnia to nieco większą ochronę, ponieważ skróty są pisane dużymi i małymi literami.

Zawsze możesz zbudować hashname w locie, jeśli przechowujesz rozszerzenie i sygnaturę czasową osobno. Jeśli odkryjesz, że tworzysz coś od czasu do czasu, możesz po prostu przechowywać to w bazie danych, aby uprościć pracę w PHP.

Lubię po prostu umieszczać hash w linku, bez rozszerzenia, nic, więc moje linki wyglądają tak.

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

Naprawdę proste, naprawdę ogólne, bezpieczne w adresach URL, zawsze w tym samym rozmiarze itp.

hashname dla tego "pliku" wyglądałby tak

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea.jpg

Jeśli masz konflikty z tym samym plikiem i innym użytkownikiem (o czym wspomniałem powyżej). Zawsze możesz dodać część sygnatury czasowej do linku, identyfikator_użytkownika lub oba. Jeśli używasz user_id, przydatne może być lewe uzupełnienie go zerami. Na przykład niektórzy użytkownicy mogą mieć ID:1 a niektóre mogą być ID:234 więc możesz zostawić go do 4 miejsc i zrobić z nich 0001 i 0234 . Następnie dodaj to do skrótu, co jest prawie niezauważalne:

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea0234.jpg

Ważną rzeczą tutaj jest to, że ponieważ sha1 to zawsze 40 a id to zawsze 4 możemy je dokładnie i łatwo oddzielić. I w ten sposób nadal możesz to wyjątkowo sprawdzić. Istnieje wiele różnych opcji, ale wiele zależy od Twoich potrzeb.

Dostęp Takich jak pobieranie. Powinieneś zawsze wyprowadzać plik z PHP, nie dawaj im bezpośredniego dostępu do pliku. Najlepszym sposobem jest przechowywanie plików poza webrootem ( nad public_html lub www teczka ). Następnie w PHP możesz ustawić właściwy typ nagłówków i po prostu odczytać plik. Działa to prawie we wszystkim z wyjątkiem wideo. Nie zajmuję się filmami, więc to temat poza moim doświadczeniem. Ale uważam, że najlepiej jest myśleć o tym, ponieważ wszystkie dane pliku to tekst, jego nagłówki, które przekształcają ten tekst w obraz, plik Excela lub pdf.

Wielką zaletą nie dawania im bezpośredniego dostępu do pliku jest to, że jeśli masz witrynę członkowską, jeśli nie chcesz, aby zawartość była dostępna bez logowania, możesz łatwo sprawdzić w PHP, czy są zalogowani, zanim udostępnisz im zawartość. A ponieważ plik znajduje się poza webrootem, nie mogą uzyskać do niego dostępu w żaden inny sposób.

Najważniejszą rzeczą jest wybranie czegoś spójnego, co jest wciąż wystarczająco elastyczne, aby sprostać wszystkim Twoim potrzebom.

Jestem pewien, że mogę wymyślić więcej, ale jeśli masz jakieś sugestie, możesz je skomentować.

PODSTAWOWY PRZEBIEG PROCESU

  1. Użytkownik przesyła formularz (enctype="multipart/form-data" )

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

  1. Serwer odbiera wiadomość z formularza, Super Globals $_POST i $_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. Sprawdź błędy if(!$_FILES['fielname']['error'])

  2. Oczyść wyświetlaną nazwę $filename = htmlentities($str, ENT_NOQUOTES, "UTF-8");

  3. Zapisz plik, utwórz rekord DB (PSUDO-CODE)

Tak:

 $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 -przesłany-plik.php

  1. Utwórz link ( różni się w zależności od routingu ), najprościej jest to zrobić w ten sposób http://www.example.com/download?file={$hash} ale jest brzydszy niż http://www.example.com/download/{$hash}

  2. użytkownik klika link prowadzi do strony pobierania.

pobierz INPUT i wyszukaj rekord

$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

Itd....

Pozdrawiam!




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. php 5.x 7.x, błąd pdo ssl:certyfikat równorzędny CN=`jakaśNazwa' nie pasuje do oczekiwanego CN='jakieśIP'

  2. KLUCZ MySQL/UNIKALNY KLUCZ

  3. Podłączanie aplikacji Heroku Lumen do Amazon RDS MySQL 5.7.19

  4. pobranie liczby całkowitej z bazy danych przy użyciu Zend Framework zwraca wartość jako ciąg

  5. Synchronizuj schemat MySQL między komputerami