Я ищу функцию php, которая будет дезинфицировать строку и сделать ее готовой к использованию для имени файла. Кто-нибудь знает о удобном?
(Я мог бы написать один, но я беспокоюсь, что я упущу персонажа!)
Изменить: для сохранения файлов в файловой системе Windows NTFS.
Вместо того, чтобы беспокоиться о том, чтобы игнорировать персонажей – как насчет использования белого списка персонажей, которые вы счастливы использовать? Например, вы можете разрешить только хорошие ol ' az
, 0-9
, _
и один экземпляр периода ( .
). Это явно более ограничивает, чем большинство файловых систем, но должно держать вас в безопасности.
Сделав небольшую корректировку решения Tor Valamo, чтобы решить проблему, замеченную Домиником Роджером, вы можете использовать:
// 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);
Как насчет использования rawurlencode ()? http://www.php.net/manual/en/function.rawurlencode.php
Вот функция, которая санирует даже китайские символы:
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("/(&)([az])([az]+;)/i", '$2', $str); $str = str_replace(' ', '-', $str); $str = rawurlencode($str); $str = str_replace('%', '-', $str); return $str; }
Вот объяснение
ОК, некоторое имя файла не будет выпущено, но в большинстве случаев оно будет работать.
ех. Оригинальное название: "საბეჭდი-და-ტიპოგრაფიული. Jpg"
Наименование выхода: "-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»
Это лучше, чем ошибка 404.
Надеюсь, это было полезно.
Карл.
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)
Добавить / удалить более допустимые символы в зависимости от того, что разрешено для вашей системы.
В качестве альтернативы вы можете попытаться создать файл, а затем вернуть ошибку, если это плохо.
Ну, tempnam () сделает это за вас.
http://us2.php.net/manual/en/function.tempnam.php
но это создает совершенно новое имя.
Чтобы дезинфицировать существующую строку, просто ограничьте то, что ваши пользователи могут ввести, и сделайте ее буквы, цифры, период, дефис и подчеркивание, затем продезинфицируйте простым регулярным выражением. Проверьте, какие символы должны быть экранированы, или вы можете получить ложные срабатывания.
$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);
Вот как вы можете санировать файловую систему по просьбе
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; }
Все остальное разрешено в файловой системе, поэтому вопрос полностью отвечает …
… но может быть опасно допускать, например, одинарные кавычки в имени файла, если вы используете его позже в небезопасном контексте HTML, потому что:
NotExist' onerror='alert(1).jpg
становится отверстием XSS :
<img src='<? echo $image ?>' /> // output: <img src='NotExist' onerror='alert(1)' />
Из-за этого популярное программное обеспечение CMS WordPress удаляет его, и они учились год за годом с помощью жесткого пути (многие отчеты об ошибках), что полезно добавлять все больше и больше символов:
$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0)); // ... a few rows later are whitespaces removed as well ... preg_replace( '/[\r\n\t -]+/', '-', $filename )
Наконец, их список включает в себя в настоящее время большинство символов, которые являются частью URI rerserved-characters и URL-адресов небезопасных символов .
Конечно, вы могли бы просто кодировать все эти символы на выходе HTML, но большинство разработчиков и меня тоже, следуйте идиоме «Лучше, чем сожалеть», и удалите их заранее.
Поэтому, наконец, я бы предложил использовать это:
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); // maximise 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; }
Все остальное, что не вызывает проблем с файловой системой, должно быть частью дополнительной функции:
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; }
И на этом этапе вам нужно сгенерировать имя файла, если результат пуст, и вы можете решить, хотите ли вы кодировать символы UTF-8. Но вам не нужно, поскольку UTF-8 разрешен во всех файловых системах, которые используются в контексте веб-хостинга.
Единственное, что вам нужно сделать, это использовать urlencode()
(как вы, надеюсь, сделаете это со всеми вашими URL-адресами), так что имя файла საბეჭდი_მანქანა.jpg
становится этим URL как ваш <img src>
или <a href>
: http: // www .maxrev.de / HTML / IMG /% E1% 83% А1% Е1% 83% 90% Е1% 83% 91% Е1% 83% 94% 83% E1% AD% E1% 83% 93% 83% E1% 98_% E1% 83% 9B% E1% 83% 90% E1% 83% 9C% E1% 83% A5% E1% 83% 90% E1% 83% 9C% E1% 83% 90.jpg
Stackoverflow делает это, поэтому я могу опубликовать эту ссылку, когда пользователь сделает это:
http://www.maxrev.de/html/img/ საბეჭდი_მანქანა. JPG
Так что это полное юридическое имя файла, а не проблема, как @ SequenceDigitale.com, упомянутый в его ответе .
Следующее выражение создает красивую, чистую и полезную строку:
/[^a-z0-9\._-]+/gi
Превращение сегодняшней финансовой: выставление счетов в сегодняшнем финансовом биллинге
Сделав небольшую корректировку решения Sean Vieira, чтобы разрешить использование одиночных точек, вы можете использовать:
preg_replace("([^\w\s\d\.\-_~,;:\[\]\(\)]|[\.]{2,})", '', $file)
РЕШЕНИЕ 1 – простой и эффективный
$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );
[^a-z0-9]+
гарантирует, что имя файла содержит только буквы и цифры '-'
сохранить имя файла Пример:
URL: http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename
РЕШЕНИЕ 2 – для очень длинных URL-адресов
Вы хотите кэшировать содержимое URL-адреса и просто должны иметь уникальные имена файлов. Я бы использовал эту функцию:
$file_name = md5( strtolower( $url ) )
это создаст имя файла с фиксированной длиной. Хэш MD5 в большинстве случаев достаточно уникален для такого использования.
Пример:
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
PHP предоставляет функцию для дезинфекции текста в другом формате
filter.filters.sanitize
Как :
echo filter_var( "Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL );
LoremIpsumhasbeentheindustry's
Они могут быть немного тяжелыми, но они достаточно гибкие, чтобы обезопасить любую строку в «безопасном» имени файла файла или имени папки (или черт, даже вычищенные слизняки и вещи, если вы согните его).
1) Создание полного имени файла (с резервным именем, если вход полностью усечен):
str_file($raw_string, $word_separator, $file_extension, $fallback_name, $length);
2) Или используя только утилиту фильтра без создания полного имени файла (строгий режим true
не позволит [] или () в имени файла):
str_file_filter($string, $separator, $strict, $length);
3) И вот эти функции:
// 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(" ", ' ', $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('Ym-d_H-m_A') . '__' . uniqid(); } elseif (empty($str)) { $str = $default; } // Return completed string if (!empty($ext)) { return $str . $ext; } else { return $str; } }
Предположим, что некоторые пользовательские данные: .....<div></div><script></script>& Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული
.....<div></div><script></script>& Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული
И мы хотим преобразовать его в нечто более дружелюбное, чтобы создать tar.gz с длиной имени файла длиной 255 символов. Вот пример использования. Примечание: этот пример включает поврежденное расширение tar.gz в качестве доказательства концепции, вы должны по-прежнему фильтровать ext после того, как строка построена против вашего белого списка (-ов).
$raw_str = '.....<div></div><script></script>& Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული'; $fallback_str = 'generated_' . date('Ym-d_H-m_A'); $bad_extension = '....t&+++a()r.gz[]'; echo str_file($raw_str, '_', $bad_extension, $fallback_str);
Вывод будет: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz
Вы можете играть здесь: https://3v4l.org/iSgi8
Или Gist: https://gist.github.com/dhaupin/b109d3a8464239b7754a
EDIT: обновленный фильтр сценариев для
вместо пространства, обновленная ссылка 3v4l
Кажется, что все это зависит от вопроса, возможно ли создать имя файла, которое можно использовать для взлома на сервере (или сделать какой-то другой ущерб). Если нет, тогда кажется, что простой ответ – это попытаться создать файл, где бы он ни был, в конечном счете, будет использоваться (так как это будет операционная система выбора, без сомнения). Позвольте операционной системе разобраться. Если он жалуется, отправьте эту жалобу обратно Пользователю в качестве ошибки проверки.
Это имеет дополнительное преимущество – быть надежно переносимым, поскольку все (я уверен,) операционные системы будут жаловаться, если имя файла неправильно сформировано для этой ОС.
Если можно сделать нечестные вещи с именем файла, возможно, есть меры, которые могут быть применены до тестирования имени файла в резидентной операционной системе – меры менее сложны, чем полная «санитария» имени файла.
в одну сторону
$bad='/[\/:*?"<>|]/'; $string = 'fi?le*'; function sanitize($str,$pat) { return preg_replace($pat,"",$str); } echo sanitize($string,$bad);
?$bad='/[\/:*?"<>|]/'; $string = 'fi?le*'; function sanitize($str,$pat) { return preg_replace($pat,"",$str); } echo sanitize($string,$bad);
/
и ..
в предоставленном пользователем файле имя файла может быть вредным. Поэтому вы должны избавиться от них чем-то вроде:
$fname = str_replace('..', '', $fname); $fname = str_replace('/', '', $fname);
Лучшее, что я знаю сегодня, – это статический метод Strings :: webalize из Nette framework.
Кстати, это переводит все диакритические знаки в их основные. Š => s ü => u ß => ss и т. Д.
Для имен файлов вы должны добавить точку "." для параметра разрешенных символов.
/** * 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; }
$ fname = str_replace ('/', '', $ fname);
Поскольку пользователи могут использовать косую черту для разделения двух слов, было бы лучше заменить тире вместо NULL