odkażacz stringów dla nazwy pliku

113

Szukam funkcji php, która oczyści ciąg i przygotuje go do użycia jako nazwa pliku. Czy ktoś zna poręczny?

(Mógłbym napisać, ale martwię się, że przeoczę jakąś postać!)

Edycja: do zapisywania plików w systemie plików Windows NTFS.

user151841
źródło
1
Czy możesz być bardziej konkretny: Co się stanie z Umlautami (usunąć lub przekonwertować na postać podstawową?) Co się stanie ze znakami specjalnymi?
Pekka,
Dla jakiego systemu plików? Różnią się. Zobacz en.wikipedia.org/wiki/…
Gordon,
Windows :) Potrzebujesz 15 znaków.
user151841
1
Zwracam uwagę, że rozwiązania z „czarnej listy” sugerowane w niektórych odpowiedziach nie są wystarczające, ponieważ nie da się sprawdzić wszystkich możliwych niepożądanych znaków (oprócz znaków specjalnych są znaki z akcentami i umlautami, całe alfabety inne niż angielskie / łacińskie, znaki sterujące itp.). Twierdzę więc, że podejście „białej listy” jest zawsze lepsze, a normalizacja ciągu (jak sugeruje komentarz Blaira McMillana do odpowiedzi Dominica Rodgera) pozwoli na naturalne traktowanie dowolnych liter z akcentami, umlautami itp.
Sean the Bean
Dobry sposób, być może przy użyciu wyrażeń regularnych, zobacz ten skrypt Pythona, który zrobiłem: github.com/gsscoder/normalize-fn
gsscoder

Odpowiedzi:

42

Zamiast martwić się o przeoczenie postaci - co powiesz na użycie białej listy postaci, z których chętnie korzystasz? Na przykład, można pozwolić tylko good ol” a-z, 0-9, _, i jedno wystąpienie okresu ( .). To oczywiście bardziej ogranicza niż większość systemów plików, ale powinno zapewnić Ci bezpieczeństwo.

Dominic Rodger
źródło
40
Nie nadaje się do języków z Umlautami. Dałoby to Qubec dla Quebecu, Dsseldorf dla Düsseldorfu i tak dalej.
Pekka,
15
Prawda - ale tak jak powiedziałem: „Na przykład”.
Dominic Rodger
5
Co może być całkowicie do przyjęcia dla PO. W przeciwnym razie użyj czegoś takiego jak php.net/manual/en/class.normalizer.php
Blair McMillan
3
Właściwie nie o to pytano. Operacja prosi o funkcję do oczyszczenia łańcucha, a nie o alternatywę.
i.am.michiel,
3
@ i.am.michiel, być może, ale biorąc pod uwagę, że OP zaakceptował to, zakładam, że uznał to za pomocne.
Dominic Rodger
157

Dokonując niewielkiej korekty rozwiązania Tor Valamo by rozwiązać problem zauważony przez Dominic Rodger, to mógłby użyć:

// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_~,;[]().
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
$file = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $file);
// Remove any runs of periods (thanks falstro!)
$file = mb_ereg_replace("([\.]{2,})", '', $file);
Sean Vieira
źródło
43
Uwielbiam ćpunów regex! -_ ~
AVProgrammer
2
@ iim.hlk - tak, brakowało nawiasu zawijającego. Dodałem je teraz. Dzięki!
Sean Vieira
2
jest tam wada, powinieneś podzielić ją na dwie części i sprawdzić ..później. Na przykład .?.skończyłoby się istnieniem ... Chociaż odkąd filtrujesz /, nie widzę, jak możesz teraz to wykorzystać, ale pokazuje, dlaczego sprawdzenie ..jest tutaj nieskuteczne. Jeszcze lepiej, nie wymieniaj, po prostu odrzuć, jeśli się nie kwalifikuje.
falstro
2
Ponieważ żadna z tych wartości nie jest nielegalna w systemie plików Windows i po co tracić więcej informacji, niż trzeba? Możesz zmienić wyrażenie regularne na proste, [^a-z0-9_-]jeśli chcesz być naprawdę restrykcyjny - lub po prostu użyć wygenerowanej nazwy i wyrzucić podaną nazwę i uniknąć wszystkich tych problemów. :-)
Sean Vieira
3
Zauważ, że: jest nielegalne.
JasonXA
50

Oto, jak możesz wyczyścić system plików zgodnie z pytaniem

function filter_filename($name) {
    // remove illegal file system characters https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
    $name = str_replace(array_merge(
        array_map('chr', range(0, 31)),
        array('<', '>', ':', '"', '/', '\\', '|', '?', '*')
    ), '', $name);
    // maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($name, PATHINFO_EXTENSION);
    $name= mb_strcut(pathinfo($name, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($name)) . ($ext ? '.' . $ext : '');
    return $name;
}

Wszystko inne jest dozwolone w systemie plików, więc odpowiedź na pytanie jest doskonała ...

... ale dopuszczenie na przykład pojedynczych cudzysłowów w nazwie pliku może być niebezpieczne, 'jeśli użyjesz go później w niebezpiecznym kontekście HTML, ponieważ ta całkowicie legalna nazwa pliku:

 ' onerror= 'alert(document.cookie).jpg

staje się otworem XSS :

<img src='<? echo $image ?>' />
// output:
<img src=' ' onerror= 'alert(document.cookie)' />

Z tego powodu popularne oprogramowanie CMS Wordpress usuwa je, ale wszystkie istotne znaki zakrywały dopiero po kilku aktualizacjach :

$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
// ... a few rows later are whitespaces removed as well ...
preg_replace( '/[\r\n\t -]+/', '-', $filename )

Wreszcie ich lista zawiera teraz większość znaków, które są częścią listy znaków zastrzeżonych URI i niebezpiecznych znaków URL .

Oczywiście możesz po prostu zakodować wszystkie te znaki na wyjściu HTML, ale większość programistów i ja też stosujemy idiom „Lepiej bezpiecznie niż przepraszam” i usuwamy je z wyprzedzeniem.

W końcu sugerowałbym użycie tego:

function filter_filename($filename, $beautify=true) {
    // sanitize filename
    $filename = preg_replace(
        '~
        [<>:"/\\|?*]|            # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
        [\x00-\x1F]|             # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
        [\x7F\xA0\xAD]|          # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
        [#\[\]@!$&\'()+,;=]|     # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
        [{}^\~`]                 # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
        ~x',
        '-', $filename);
    // avoids ".", ".." or ".hiddenFiles"
    $filename = ltrim($filename, '.-');
    // optional beautification
    if ($beautify) $filename = beautify_filename($filename);
    // maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
    return $filename;
}

Wszystko inne, co nie powoduje problemów z systemem plików, powinno być częścią dodatkowej funkcji:

function beautify_filename($filename) {
    // reduce consecutive characters
    $filename = preg_replace(array(
        // "file   name.zip" becomes "file-name.zip"
        '/ +/',
        // "file___name.zip" becomes "file-name.zip"
        '/_+/',
        // "file---name.zip" becomes "file-name.zip"
        '/-+/'
    ), '-', $filename);
    $filename = preg_replace(array(
        // "file--.--.-.--name.zip" becomes "file.name.zip"
        '/-*\.-*/',
        // "file...name..zip" becomes "file.name.zip"
        '/\.{2,}/'
    ), '.', $filename);
    // lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
    $filename = mb_strtolower($filename, mb_detect_encoding($filename));
    // ".file-name.-" becomes "file-name"
    $filename = trim($filename, '.-');
    return $filename;
}

W tym momencie musisz wygenerować nazwę pliku, jeśli wynik jest pusty i możesz zdecydować, czy chcesz zakodować znaki UTF-8. Ale nie jest to konieczne, ponieważ UTF-8 jest dozwolony we wszystkich systemach plików używanych w kontekstach hostingu internetowego.

Jedyne, co musisz zrobić, to użyć urlencode()(jak mamy nadzieję, że robisz to ze wszystkimi adresami URL), aby nazwa pliku საბეჭდი_მანქანა.jpgstała się tym adresem URL jako Twój <img src>lub <a href>: http://www.maxrev.de/html/img/%E1%83% A1% E1% 83% 90% E1% 83% 91% E1% 83% 94% E1% 83% AD% E1% 83% 93% E1% 83% 98_% E1% 83% 9B% E1% 83% 90% E1% 83% 9C% E1% 83% A5% E1% 83% 90% E1% 83% 9C% E1% 83% 90.jpg

Stackoverflow to robi, więc mogę opublikować ten link tak, jak zrobiłby to użytkownik:
http://www.maxrev.de/html/img/ საბეჭდი_მანქანა. Jpg

Jest to więc pełna legalna nazwa pliku, a nie problem, o czym @ SequenceDigitale.com wspomniał w swojej odpowiedzi .

mgutt
źródło
3
Dobra robota. Najbardziej pomocna odpowiedź dla mnie. +1
Och ... Funkcja działa dobrze, ale od jakiegoś czasu zaczęła umieszczać - między każdą postacią, jak r-u-l-e-si nie mam pojęcia, dlaczego tak się dzieje. Jasne, że nie jest to wina funkcji, ale pytanie - co może być przyczyną takiego zachowania? Niewłaściwe kodowanie?
1
No cóż ... Właśnie zrobiłem debug i dzieje się to zaraz po preg_replacein filter_filename().
Po usunięciu tych komentarzy zaczął ponownie działać.
Które komentarze usunąłeś? Wyślij mi e-mail, jeśli jest to łatwiejsze: gutt.it/contact.htm
mgutt
43

A co z użyciem rawurlencode ()? http://www.php.net/manual/en/function.rawurlencode.php

Oto funkcja, która oczyszcza nawet chińskie znaki:

public static function normalizeString ($str = '')
{
    $str = strip_tags($str); 
    $str = preg_replace('/[\r\n\t ]+/', ' ', $str);
    $str = preg_replace('/[\"\*\/\:\<\>\?\'\|]+/', ' ', $str);
    $str = strtolower($str);
    $str = html_entity_decode( $str, ENT_QUOTES, "utf-8" );
    $str = htmlentities($str, ENT_QUOTES, "utf-8");
    $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
    $str = str_replace(' ', '-', $str);
    $str = rawurlencode($str);
    $str = str_replace('%', '-', $str);
    return $str;
}

Oto wyjaśnienie

  1. Usuń znaczniki HTML
  2. Usuń przerwę / tabulatory / powrót karetki
  3. Usuń niedozwolone znaki dla folderu i nazwy pliku
  4. Umieść sznurek małymi literami
  5. Usuń obce akcenty, takie jak Éàû, konwertując je na encje HTML, a następnie usuń kod i zachowaj literę.
  6. Zastąp spacje myślnikami
  7. Zakoduj znaki specjalne, które mogą przejść poprzednie kroki i wprowadź konfliktową nazwę pliku na serwerze. dawny. „中文 百强 网”
  8. Zastąp „%” myślnikami, aby mieć pewność, że łącze do pliku nie zostanie przepisane przez przeglądarkę podczas odpytywania pliku.

OK, niektóre nazwy plików nie będą ważne, ale w większości przypadków będą działać.

dawny. Oryginalna nazwa: „საბეჭდი-და-ტიპოგრაფიული. Jpg”

Nazwa wyjścia: "-E1-83-A1-E1-83-90-E1-83-91-E1-83-94-E1-83-AD-E1-83-93-E1-83-98 - E1- 83-93-E1-83-90 - E1-83-A2-E1-83-98-E1-83-9E-E1-83-9D-E1-83-92-E1-83-A0-E1-83 -90-E1-83-A4-E1-83-98-E1-83-A3-E1-83-9A-E1-83-98.jpg ”

To lepsze niż błąd 404.

Mam nadzieję, że to było pomocne.

Carl.

SequenceDigitale.com
źródło
1
Nie usuwasz znaków NULL i Control. Wszystkie znaki ASCII od 0 do 32 powinny zostać usunięte z ciągu.
Basil Musa
UTF-8 jest dozwolony w systemie plików i dozwolony w adresach URL, więc dlaczego miałby powodować błąd 404? Jedyną rzeczą, którą musisz zrobić, to do kodowania URL http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpg, aby http://www.maxrev.de/html/img/%E1%83%A1%E1%83%90%E1%83%91%E1%83%94%E1%83%AD%E1%83%93%E1%83%98_%E1%83%9B%E1%83%90%E1%83%9C%E1%83%A5%E1%83%90%E1%83%9C%E1%83%90.jpgw kodzie źródłowym HTML, jak mamy nadzieję zrobić ze wszystkimi swoimi adresami URL.
mgutt
1
Kilka innych punktów: usuwasz tagi HTML, strip_tags()a następnie usuwasz [<>]. Przez to strip_tags()tak naprawdę nie jest w ogóle potrzebne. Ten sam punkt to cytaty. Po dekodowaniu za pomocą nie ma żadnych cudzysłowów ENT_QUOTES. I str_replace()nie usuwa kolejnych białych spacji, a następnie używasz strtolower()dla ciągu wielobajtowego. I dlaczego w ogóle konwertujesz na małe litery? I w końcu nie złapałeś żadnej zarezerwowanej postaci, jak wspomniał @BasilMusa. Więcej szczegółów w mojej odpowiedzi: stackoverflow.com/a/42058764/318765
mgutt
zakochałem się w tym!
Yash Kumar Verma
39

ROZWIĄZANIE 1 - proste i skuteczne

$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );

  • strtolower () gwarantuje, że nazwa pliku jest zapisana małymi literami (ponieważ wielkość liter nie ma znaczenia w adresie URL, ale w nazwie pliku NTFS)
  • [^a-z0-9]+ upewni się, że nazwa pliku zawiera tylko litery i cyfry
  • Zastąp nieprawidłowe znaki '-'słowami, aby zachować czytelność nazwy pliku

Przykład:

URL:  http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename
File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename

ROZWIĄZANIE 2 - dla bardzo długich adresów URL

Chcesz przechowywać zawartość adresu URL w pamięci podręcznej i po prostu mieć unikalne nazwy plików. Użyłbym tej funkcji:

$file_name = md5( strtolower( $url ) )

stworzy to nazwę pliku o stałej długości. Skrót MD5 jest w większości przypadków wystarczająco wyjątkowy dla tego rodzaju zastosowań.

Przykład:

URL:  https://www.amazon.com/Interstellar-Matthew-McConaughey/dp/B00TU9UFTS/ref=s9_nwrsa_gw_g318_i10_r?_encoding=UTF8&fpl=fresh&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=desktop-1&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_t=36701&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_i=desktop
File: 51301f3edb513f6543779c3a5433b01c
Philipp
źródło
4
Może MD5 może rozwiązać problem: zachowaj ostrożność podczas używania skrótów z adresami URL. Chociaż pierwiastek kwadratowy z liczby skrenta.com/2007/08/md5_tutorial.html adresów URL jest nadal dużo większy niż obecny rozmiar strony internetowej, jeśli wystąpi kolizja, strony o Britney Spears będą wyświetlane w czasie, gdy spodziewałeś się stron o Bugzilli. W naszym przypadku prawdopodobnie nie stanowi to problemu, ale w przypadku miliardów stron wybrałbym znacznie większy algorytm haszujący, taki jak SHA 256, lub całkowicie go uniknął. Źródło: boyter.org/2013/01/code-for-a-search-engine-in-php-part-1
adilbo
15

Cóż, tempnam () zrobi to za Ciebie.

http://us2.php.net/manual/en/function.tempnam.php

ale to tworzy zupełnie nową nazwę.

Aby wyczyścić istniejący ciąg, po prostu ogranicz to, co użytkownicy mogą wprowadzać, i nadaj mu litery, cyfry, kropkę, łącznik i podkreślenie, a następnie wyczyść za pomocą prostego wyrażenia regularnego. Sprawdź, jakie znaki należy zmienić lub możesz uzyskać fałszywe alarmy.

$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);
Mark Moline
źródło
13
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)

Dodaj / usuń więcej poprawnych znaków w zależności od tego, co jest dozwolone w twoim systemie.

Alternatywnie możesz spróbować utworzyć plik, a następnie zwrócić błąd, jeśli jest zły.

Tor Valamo
źródło
5
Pozwoliłoby to na użycie nazw plików takich jak .., co może, ale nie musi, stanowić problem.
Dominic Rodger
@Dom - po prostu sprawdź to osobno, ponieważ jest to stała wartość.
Tor Valamo,
10

PHP udostępnia funkcję oczyszczania tekstu do innego formatu

filter.filters.sanitize

Jak :

echo filter_var(
   "Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL
); 

Zablokować cytat LoremIpsumhasbeentheindustry's

120DEV
źródło
1
Dobrze, ale nie usunie ukośników, co może być problemem: przechodzenie przez katalog.
func0der
7

bezpieczny: zamień każdą sekwencję NIE „a-zA-Z0-9_-” na myślnik; dodaj rozszerzenie samodzielnie.

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).'.'.$extension;
pospolity
źródło
1
Musisz dodać rozszerzenie pliku oddzielone „.”: $ Name = preg_replace ('/ [^ a-zA-Z0-9 _-] + /', '-', strtolower ($ name)). '.' , rozszerzenie $;
Smith
6

Poniższe wyrażenie tworzy ładny, czysty i użyteczny ciąg:

/[^a-z0-9\._-]+/gi

Zmieniamy dzisiejsze finanse: rozliczenia w dzisiejsze-finansowe-rozliczenia

Sampson
źródło
więc nazwa pliku nie może zawierać kropki, podkreślenia ani czegoś podobnego?
Tor Valamo,
2
@Jonathan - co z kursywą?
Dominic Rodger
@Tor, tak, przepraszam. Zaktualizowano. @Dominic, po prostu podkreślam tekst.
Sampson,
Co to jest gism? Otrzymuję „Ostrzeżenie: preg_replace () [function.preg-replace]: Nieznany modyfikator 'g'”
user151841
1
@ user151841 Dla preg_replaceflagi globalnej jest niejawna. Więc nie ma potrzeby stosowania g, jeśli używany jest preg_replace. Kiedy chcemy kontrolować liczbę podmian, preg_replace ma do tego limitparametr. Przeczytaj dokumentację preg_replace, aby uzyskać więcej informacji.
rineez
6

Dokonując niewielkiej korekty w rozwiązaniu Seana Vieiry, aby uwzględnić pojedyncze kropki, możesz użyć:

preg_replace("([^\w\s\d\.\-_~,;:\[\]\(\)]|[\.]{2,})", '', $file)
CarlJohnson
źródło
2

Mogą być trochę ciężkie, ale są wystarczająco elastyczne, aby odkażać dowolny ciąg do „bezpiecznego” en nazwie pliku lub folderu (lub do cholery, nawet wyczyszczone ślimaki i inne rzeczy, jeśli je zgniesz).

1) Budowanie pełnej nazwy pliku (z nazwą zastępczą w przypadku całkowitego obcięcia danych wejściowych):

str_file($raw_string, $word_separator, $file_extension, $fallback_name, $length);

2) Lub używając samego filtra używanego bez budowania pełnej nazwy pliku (tryb ścisły truenie zezwala na [] lub () w nazwie pliku):

str_file_filter($string, $separator, $strict, $length);

3) A oto te funkcje:

// Returns filesystem-safe string after cleaning, filtering, and trimming input
function str_file_filter(
    $str,
    $sep = '_',
    $strict = false,
    $trim = 248) {

    $str = strip_tags(htmlspecialchars_decode(strtolower($str))); // lowercase -> decode -> strip tags
    $str = str_replace("%20", ' ', $str); // convert rogue %20s into spaces
    $str = preg_replace("/%[a-z0-9]{1,2}/i", '', $str); // remove hexy things
    $str = str_replace("&nbsp;", ' ', $str); // convert all nbsp into space
    $str = preg_replace("/&#?[a-z0-9]{2,8};/i", '', $str); // remove the other non-tag things
    $str = preg_replace("/\s+/", $sep, $str); // filter multiple spaces
    $str = preg_replace("/\.+/", '.', $str); // filter multiple periods
    $str = preg_replace("/^\.+/", '', $str); // trim leading period

    if ($strict) {
        $str = preg_replace("/([^\w\d\\" . $sep . ".])/", '', $str); // only allow words and digits
    } else {
        $str = preg_replace("/([^\w\d\\" . $sep . "\[\]\(\).])/", '', $str); // allow words, digits, [], and ()
    }

    $str = preg_replace("/\\" . $sep . "+/", $sep, $str); // filter multiple separators
    $str = substr($str, 0, $trim); // trim filename to desired length, note 255 char limit on windows

    return $str;
}


// Returns full file name including fallback and extension
function str_file(
    $str,
    $sep = '_',
    $ext = '',
    $default = '',
    $trim = 248) {

    // Run $str and/or $ext through filters to clean up strings
    $str = str_file_filter($str, $sep);
    $ext = '.' . str_file_filter($ext, '', true);

    // Default file name in case all chars are trimmed from $str, then ensure there is an id at tail
    if (empty($str) && empty($default)) {
        $str = 'no_name__' . date('Y-m-d_H-m_A') . '__' . uniqid();
    } elseif (empty($str)) {
        $str = $default;
    }

    // Return completed string
    if (!empty($ext)) {
        return $str . $ext;
    } else {
        return $str;
    }
}

Powiedzmy, że niektóre dane wejściowe użytkownika to: .....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული

Chcemy przekonwertować go na coś bardziej przyjaznego, aby utworzyć plik tar.gz z nazwą pliku o długości 255 znaków. Oto przykład użycia. Uwaga: ten przykład zawiera zniekształcone rozszerzenie tar.gz jako dowód słuszności koncepcji, nadal należy filtrować rozszerzenie po utworzeniu łańcucha znaków na białej liście (listach).

$raw_str = '.....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name  %20   %20 %21 %2C Décor  \/.  /. .  z \... y \...... x ./  “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული';
$fallback_str = 'generated_' . date('Y-m-d_H-m_A');
$bad_extension = '....t&+++a()r.gz[]';

echo str_file($raw_str, '_', $bad_extension, $fallback_str);

Wynik byłby następujący: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

Możesz się nim bawić tutaj: https://3v4l.org/iSgi8

Lub Gist: https://gist.github.com/dhaupin/b109d3a8464239b7754a

EDYCJA: zaktualizowany filtr skryptów &nbsp;zamiast spacji, zaktualizowany link 3v4l

dhaupin
źródło
1

Najlepsze, co dziś znam, to statyczna metoda Strings :: webalize z frameworka Nette.

Przy okazji, to tłumaczy wszystkie znaki diakrytyczne na ich podstawowe .. š => s ü => u ß => ss itd.

W przypadku nazw plików musisz dodać kropkę „.” parametr dozwolonych znaków.

/**
 * Converts to ASCII.
 * @param  string  UTF-8 encoding
 * @return string  ASCII
 */
public static function toAscii($s)
{
    static $transliterator = NULL;
    if ($transliterator === NULL && class_exists('Transliterator', FALSE)) {
        $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
    }

    $s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
    $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
    $s = str_replace(
        array("\xE2\x80\x9E", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x9A", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xB0"),
        array("\x03", "\x03", "\x03", "\x02", "\x02", "\x02", "\x04"), $s
    );
    if ($transliterator !== NULL) {
        $s = $transliterator->transliterate($s);
    }
    if (ICONV_IMPL === 'glibc') {
        $s = str_replace(
            array("\xC2\xBB", "\xC2\xAB", "\xE2\x80\xA6", "\xE2\x84\xA2", "\xC2\xA9", "\xC2\xAE"),
            array('>>', '<<', '...', 'TM', '(c)', '(R)'), $s
        );
        $s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); // intentionally @
        $s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
            . "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
            . "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
            . "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
            . "\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
            'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.');
        $s = preg_replace('#[^\x00-\x7F]++#', '', $s);
    } else {
        $s = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); // intentionally @
    }
    $s = str_replace(array('`', "'", '"', '^', '~', '?'), '', $s);
    return strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
}


/**
 * Converts to web safe characters [a-z0-9-] text.
 * @param  string  UTF-8 encoding
 * @param  string  allowed characters
 * @param  bool
 * @return string
 */
public static function webalize($s, $charlist = NULL, $lower = TRUE)
{
    $s = self::toAscii($s);
    if ($lower) {
        $s = strtolower($s);
    }
    $s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
    $s = trim($s, '-');
    return $s;
}
DnD
źródło
Dlaczego chcesz zastąpić znaki diakrytyczne? Po prostu użyj urlencode()przed użyciem nazwy pliku jako srclub href. Jedynym obecnie stosowany system plików, który ma problemy z UTF-8 jest FATx (używany przez XBOX): en.wikipedia.org/wiki/Comparison_of_file_systems#Limits I nie sądzę, że to jest wykorzystywane przez serwery WWW
mgutt
1

Wygląda na to, że to wszystko zależy od pytania, czy możliwe jest utworzenie nazwy pliku, której można by użyć do włamania się do serwera (lub wyrządzenia takich innych szkód). Jeśli nie, to wydaje się, że prostą odpowiedzią jest próba utworzenia pliku tam, gdzie ostatecznie będzie on używany (ponieważ bez wątpienia będzie to wybrany system operacyjny). Pozwól systemowi operacyjnemu to rozwiązać. Jeśli narzeka, zgłoś tę skargę z powrotem do użytkownika jako błąd walidacji.

Ma to dodatkową zaletę, że jest niezawodnie przenośne, ponieważ wszystkie (jestem prawie pewien) systemy operacyjne będą narzekać, jeśli nazwa pliku nie zostanie poprawnie utworzona dla tego systemu operacyjnego.

Jeśli jest to możliwe do zrobienia nikczemne rzeczy z nazwy pliku, być może istnieją środki, które mogą być zastosowane przed badaniem nazwę pliku w systemie operacyjnym rezydenta - środki mniej skomplikowane niż pełny „sanitarnych” w nazwie.

ReverseEMF
źródło
0

jednokierunkowa

$bad='/[\/:*?"<>|]/';
$string = 'fi?le*';

function sanitize($str,$pat)
{
    return preg_replace($pat,"",$str);

}
echo sanitize($string,$bad);
ghostdog74
źródło
A co ze znakami niedrukowalnymi? W tym przypadku lepiej jest użyć podejścia białej listy niż czarnej listy. Zasadniczo zezwalaj tylko na nazwy plików ASCII, które można wydrukować, oczywiście z wyłączeniem specjalnych liter. Ale w przypadku języków innych niż angielskie to kolejny problem.
TheRealChx101
0

/a ..nazwa pliku podana przez użytkownika może być szkodliwa. Więc powinieneś się ich pozbyć przez coś takiego:

$fname = str_replace('..', '', $fname);
$fname = str_replace('/',  '', $fname);
koniec gry
źródło
To jest niewystarczające! Na przykład nazwa pliku „./.name” nadal będzie usuwać z bieżącego katalogu. (Usunięcie ... nic tutaj nie robi, ale usunięcie / zamieni ./. W ... i tym samym
wyłamie
3
@ cemper93 Nie, ta odpowiedź po prostu zmieni ciąg znaków, na ..namektóry nic by się nie wyrwało . Usunięcie wszystkich znaków separatora ścieżek powinno wystarczyć, aby zapobiec przechodzeniu do katalogu. (Usunięcie ..jest technicznie niepotrzebne.)
cdhowie
@cdhowie Tak, ale nazwa pliku ./.staje ... I wreszcie ta odpowiedź pomija wszystkie inne zastrzeżone znaki systemu plików, takie jak NULL. Więcej w mojej odpowiedzi: stackoverflow.com/a/42058764/318765
mgutt
-4

$ fname = str_replace ('/', '', $ fname);

Ponieważ użytkownicy mogą używać ukośnika do oddzielania dwóch słów, lepiej byłoby zastąpić je myślnikiem zamiast NULL

user2246924
źródło
Gdzie jest powiedziane, że zastąpi go NULL? Ponadto nie obsługuje to wszystkich znaków specjalnych.
Travis Pessetto
Tak - istnieją inne znaki specjalne, które również wymagają obsługi. str_replace i tak nie będzie tutaj najlepszą ofertą.
Martin Kovachev