Дезинфицирующее средство для строки для имени файла

Я ищу функцию php, которая будет дезинфицировать строку и сделать ее готовой к использованию для имени файла. Кто-нибудь знает о удобном?

(я мог бы написать один, но я беспокоюсь, что я упущу персонажа!)

Изменить: для сохранения файлов в файловой системе Windows NTFS.

Ответ 1

Вместо того, чтобы беспокоиться о пропущенных персонажах - как насчет использования белого списка персонажей, которым вы счастливы использовать? Например, вы можете разрешить только хорошие ol a-z, 0-9, _ и один экземпляр периода (.). Это явно более ограничивает, чем большинство файловых систем, но должно быть безопасным.

Ответ 2

Сделав небольшую корректировку решения 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);

Ответ 3

Как насчет использования 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("/(&)([a-z])([a-z]+;)/i", '$2', $str);
    $str = str_replace(' ', '-', $str);
    $str = rawurlencode($str);
    $str = str_replace('%', '-', $str);
    return $str;
}

Вот объяснение

  • Разделите HTML-теги
  • Удалить разрыв/вкладки/обратную перевозку
  • Удалить недопустимые символы для папки и имени файла
  • Поместите строку в нижний регистр
  • Удалите внешние акценты, такие как Éàû, преобразуйте их в html-объекты, а затем удалите код и сохраните букву.
  • Заменить пространства с тире
  • Кодировать специальные символы, которые могли бы передавать предыдущие шаги и вводить имя файла конфликта на сервере. ех. "中文 百强 网"
  • Замените "%" тире, чтобы убедиться, что ссылка на файл не будет перезаписана браузером при запросе файла.

ОК, некоторое имя файла не будет выпущено, но в большинстве случаев оно будет работать.

ех. Оригинальное название: "საბეჭდი-და-ტიპოგრაფიული. 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.

Надеюсь, что это было полезно.

Карл.

Ответ 4

РЕШЕНИЕ 1 - простой и эффективный

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

  • strtolower() гарантирует, что имя файла имеет нижний регистр (поскольку регистр не имеет значения внутри URL-адреса, но в имени файла NTFS)
  • [^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

Ответ 5

Вот как вы можете санировать файловую систему по просьбе

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 контексте, потому что это абсолютно легально файла:

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

становится отверстием XSS:

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

Из-за этого популярное программное обеспечение 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);
    // 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;
}

Все остальное, что не вызывает проблем с файловой системой, должно быть частью дополнительной функции:

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, упомянутый в его ответе.

Ответ 6

preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)

Добавить/удалить более допустимые символы в зависимости от того, что разрешено для вашей системы.

В качестве альтернативы вы можете попытаться создать файл, а затем вернуть ошибку, если это плохо.

Ответ 7

Хорошо, tempnam() сделает это за вас.

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

но это создает совершенно новое имя.

Чтобы очистить существующую строку, просто ограничьте то, что могут вводить ваши пользователи, и сделайте ее буквы, цифры, период, дефис и подчеркивание, затем выполните санирование простым регулярным выражением. Проверьте, какие символы должны быть экранированы, или вы можете получить ложные срабатывания.

$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);

Ответ 8

PHP предоставляет функцию для дезинфекции текста в другом формате

filter.filters.sanitize

Как:

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

LoremIpsumhasbeentheindustry's

Ответ 9

Следующее выражение создает красивую, чистую и полезную строку:

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

Превращение сегодня финансовых: выставление счетов в сегодняшнее финансовое биллинг

Ответ 10

Сделав небольшую корректировку решения Sean Vieira, чтобы разрешить использование одиночных точек, вы можете использовать:

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

Ответ 11

Они могут быть немного тяжелыми, но они достаточно гибкие, чтобы обезопасить любую строку в "безопасном" типе имени файла или имени папки en (или черт, даже вычищенные слизняки и вещи, если вы его согните).

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("&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;
    }
}

Итак, скажем, какой-то пользовательский ввод: .....&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) საბეჭდი-და-ტიპოგრაფიული

И мы хотим преобразовать его в нечто более дружелюбное, чтобы создать tar.gz с длиной имени файла длиной 255 символов. Вот пример использования. Примечание: этот пример включает поврежденное расширение tar.gz в качестве доказательства концепции, вы должны по-прежнему фильтровать ext после того, как строка построена против вашего белого списка.

$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);

Выход: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

Вы можете играть здесь: https://3v4l.org/iSgi8

Или a Gist: https://gist.github.com/dhaupin/b109d3a8464239b7754a

EDIT: обновленный script фильтр для &nbsp; вместо пространства, обновленная ссылка 3v4l

Ответ 12

Лучшее, что я знаю сегодня, - это статический метод Strings:: webalize из рамок Nette.

BTW, это переводит все диакритические знаки в их основные. š = > 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;
}

Ответ 13

Кажется, что все зависит от вопроса: возможно ли создать имя файла, которое можно использовать для взлома на сервере (или сделать какой-то другой ущерб). Если нет, тогда кажется, что простой ответ - это попытаться создать файл, где бы он ни был, в конечном счете, будет использоваться (так как это будет операционная система выбора, без сомнения). Позвольте операционной системе разобраться. Если он жалуется, отправьте эту жалобу обратно пользователю в качестве ошибки проверки.

Это имеет дополнительное преимущество - быть надежно переносимым, поскольку все (я уверен,) операционные системы будут жаловаться, если имя файла неправильно сформировано для этой ОС.

Если можно делать нецензурные вещи с именем файла, возможно, есть меры, которые могут быть применены перед тестированием имени файла в резидентной операционной системе - меньше, чем полная "санитария" имени файла.

Ответ 14

/ и .. в указанном пользователем имени файла могут быть вредными. Поэтому вы должны избавиться от них чем-то вроде:

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

Ответ 15

в одну сторону

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

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

}
echo sanitize($string,$bad);

Ответ 16

безопасно: заменить каждую последовательность НЕ "a-zA-Z0-9_-" на тире; добавить расширение самостоятельно.

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).$extension;

Ответ 17

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

Поскольку пользователи могут использовать косую черту для разделения двух слов, было бы лучше заменить тире вместо NULL