Sanitizing строки, чтобы сделать их URL и имя файла безопасным?

Я пытаюсь придумать функцию, которая делает хорошую работу по дезинфекции определенных строк, чтобы они были безопасны для использования в URL-адресе (например, в пул сообщений), а также безопасно использовать в качестве имен файлов. Например, когда кто-то загружает файл, я хочу убедиться, что я удалю все опасные символы из имени.

До сих пор я придумал следующую функцию, которая, я надеюсь, решает эту проблему и позволяет использовать и зарубежные данные UTF-8.

/** * Convert a string to the file/URL safe "slug" form * * @param string $string the string to clean * @param bool $is_filename TRUE will allow additional filename characters * @return string */ function sanitize($string = '', $is_filename = FALSE) { // Replace all weird characters with dashes $string = preg_replace('/[^\w\-'. ($is_filename ? '~_\.' : ''). ']+/u', '-', $string); // Only allow one dash separator at a time (and make string lowercase) return mb_strtolower(preg_replace('/--+/u', '-', $string), 'UTF-8'); } 

У кого-нибудь есть какие-то хитрые данные о пробных данных, которые я могу использовать против этого – или узнать лучший способ защитить наши приложения от плохих имен?

$ is-filename позволяет некоторым дополнительным символам, таким как temp vim files

update: удалил символ звезды, так как я не мог думать о действительном использовании

Некоторые замечания по вашему решению:

  1. 'u' в конце вашего шаблона означает, что шаблон , а не соответствующий ему текст, будет интерпретироваться как UTF-8 (я полагаю, вы приняли последний?).
  2. \ w соответствует символу подчеркивания. Вы специально указываете его для файлов, что приводит к предположению, что вы не хотите их в URL-адресах, но в коде, которому у вас есть URL-адреса, будет разрешено включать символ подчеркивания.
  3. Включение «иностранного UTF-8», по-видимому, зависит от региона. Неясно, является ли это локалью сервера или клиента. Из документов PHP:

Символом «слово» является любая буква или цифра или символ подчеркивания, то есть любой символ, который может быть частью слова «Perl». Определение букв и цифр контролируется таблицами символов PCRE и может варьироваться в зависимости от соответствия языкового соответствия. Например, в «fr» (французском) языке, для символов с акцентом используются некоторые коды символов, превышающие 128, и они сопоставляются символом \ w.

Создание пули

Вы, вероятно, не должны включать акцентированные и т. Д. Символы в своем сообщении, так как технически они должны быть закодированы в процентах (для каждого правила кодирования URL), поэтому у вас будут уродливые URL-адреса.

Итак, если бы я был вами, после нижнего индекса, я бы превратил любые «специальные» символы в их эквивалент (например, é -> e) и заменил символы non [az] на «-», ограничивая прогоны одного «-», как вы это сделали. Здесь реализована реализация специальных символов: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Санитария в целом

OWASP имеет PHP-версию своего Enterprise Security API, который среди прочего включает в себя методы безопасного кодирования и декодирования ввода и вывода в вашем приложении.

Интерфейс Encoder обеспечивает:

 canonicalize (string $input, [bool $strict = true]) decodeFromBase64 (string $input) decodeFromURL (string $input) encodeForBase64 (string $input, [bool $wrap = false]) encodeForCSS (string $input) encodeForHTML (string $input) encodeForHTMLAttribute (string $input) encodeForJavaScript (string $input) encodeForOS (Codec $codec, string $input) encodeForSQL (Codec $codec, string $input) encodeForURL (string $input) encodeForVBScript (string $input) encodeForXML (string $input) encodeForXMLAttribute (string $input) encodeForXPath (string $input) 

https://github.com/OWASP/PHP-ESAPI https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API

Я нашел эту большую функцию в коде Chyrp :

 /** * Function: sanitize * Returns a sanitized string, typically for URLs. * * Parameters: * $string - The string to sanitize. * $force_lowercase - Force the string to lowercase? * $anal - If set to *true*, will remove all non-alphanumeric characters. */ function sanitize($string, $force_lowercase = true, $anal = false) { $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]", "}", "\\", "|", ";", ":", "\"", "'", "&#8216;", "&#8217;", "&#8220;", "&#8221;", "&#8211;", "&#8212;", "—", "–", ",", "<", ".", ">", "/", "?"); $clean = trim(str_replace($strip, "", strip_tags($string))); $clean = preg_replace('/\s+/', "-", $clean); $clean = ($anal) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ; return ($force_lowercase) ? (function_exists('mb_strtolower')) ? mb_strtolower($clean, 'UTF-8') : strtolower($clean) : $clean; } 

и этот в коде WordPress

 /** * Sanitizes a filename replacing whitespace with dashes * * Removes special characters that are illegal in filenames on certain * operating systems and special characters requiring special escaping * to manipulate at the command line. Replaces spaces and consecutive * dashes with a single dash. Trim period, dash and underscore from beginning * and end of filename. * * @since 2.1.0 * * @param string $filename The filename to be sanitized * @return string The sanitized filename */ function sanitize_file_name( $filename ) { $filename_raw = $filename; $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}"); $special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw); $filename = str_replace($special_chars, '', $filename); $filename = preg_replace('/[\s-]+/', '-', $filename); $filename = trim($filename, '.-_'); return apply_filters('sanitize_file_name', $filename, $filename_raw); } 

Обновление сентябрь 2012 г.

Alix Axel проделал невероятную работу в этой области. Его структура фреймов включает в себя несколько отличных текстовых фильтров и преобразований.

  • Unaccent
  • слизень
  • Фильтр

Это должно сделать ваши имена файлов безопасными …

 $string = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $string); 

и более глубокое решение этого:

 // Remove special accented characters - ie. sí. $clean_name = strtr($string, array('Š' => 'S','Ž' => 'Z','š' => 's','ž' => 'z','Ÿ' => 'Y','À' => 'A','Á' => 'A','Â' => 'A','Ã' => 'A','Ä' => 'A','Å' => 'A','Ç' => 'C','È' => 'E','É' => 'E','Ê' => 'E','Ë' => 'E','Ì' => 'I','Í' => 'I','Î' => 'I','Ï' => 'I','Ñ' => 'N','Ò' => 'O','Ó' => 'O','Ô' => 'O','Õ' => 'O','Ö' => 'O','Ø' => 'O','Ù' => 'U','Ú' => 'U','Û' => 'U','Ü' => 'U','Ý' => 'Y','à' => 'a','á' => 'a','â' => 'a','ã' => 'a','ä' => 'a','å' => 'a','ç' => 'c','è' => 'e','é' => 'e','ê' => 'e','ë' => 'e','ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ñ' => 'n','ò' => 'o','ó' => 'o','ô' => 'o','õ' => 'o','ö' => 'o','ø' => 'o','ù' => 'u','ú' => 'u','û' => 'u','ü' => 'u','ý' => 'y','ÿ' => 'y')); $clean_name = strtr($clean_name, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u')); $clean_name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $clean_name); Международная система // Remove special accented characters - ie. sí. $clean_name = strtr($string, array('Š' => 'S','Ž' => 'Z','š' => 's','ž' => 'z','Ÿ' => 'Y','À' => 'A','Á' => 'A','Â' => 'A','Ã' => 'A','Ä' => 'A','Å' => 'A','Ç' => 'C','È' => 'E','É' => 'E','Ê' => 'E','Ë' => 'E','Ì' => 'I','Í' => 'I','Î' => 'I','Ï' => 'I','Ñ' => 'N','Ò' => 'O','Ó' => 'O','Ô' => 'O','Õ' => 'O','Ö' => 'O','Ø' => 'O','Ù' => 'U','Ú' => 'U','Û' => 'U','Ü' => 'U','Ý' => 'Y','à' => 'a','á' => 'a','â' => 'a','ã' => 'a','ä' => 'a','å' => 'a','ç' => 'c','è' => 'e','é' => 'e','ê' => 'e','ë' => 'e','ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ñ' => 'n','ò' => 'o','ó' => 'o','ô' => 'o','õ' => 'o','ö' => 'o','ø' => 'o','ù' => 'u','ú' => 'u','û' => 'u','ü' => 'u','ý' => 'y','ÿ' => 'y')); $clean_name = strtr($clean_name, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u')); $clean_name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $clean_name); 

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

 $clean_name = strtolower($clean_name); 

для последней строки.

Попробуй это:

 function normal_chars($string) { $string = htmlentities($string, ENT_QUOTES, 'UTF-8'); $string = preg_replace('~&([az]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', $string); $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8'); $string = preg_replace(array('~[^0-9a-z]~i', '~[ -]+~'), ' ', $string); return trim($string, ' -'); } Examples: echo normal_chars('Álix----_Ãxel!?!?'); // Alix Axel echo normal_chars('áéíóúÁÉÍÓÚ'); // aeiouAEIOU echo normal_chars('üÿÄËÏÖÜŸåÅ'); // uyAEIOUYaA 

На основе выбранного ответа в этом потоке: URL Friendly Username в PHP?

Это не совсем ответ, так как он не предоставляет никаких решений (пока!), Но он слишком велик, чтобы вписаться в комментарий …


Я провел некоторое тестирование (относительно имен файлов) в Windows 7 и Ubuntu 12.04, и я узнал, что:

1. PHP не может обрабатывать имена файлов, отличных от ASCII.

Хотя и Windows, и Ubuntu могут обрабатывать имена файлов Unicode (даже RTL, как кажется) PHP 5.3 требует, чтобы хаки имели дело даже с обычным старым ISO-8859-1, поэтому лучше держать его ASCII только для безопасности.

2. Длина имени файла (особенно в Windows)

В Ubuntu максимальная длина, которую может иметь имя файла (включая расширение), равна 255 (исключая путь):

 /var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/ 

Однако в Windows 7 (NTFS) максимальная длина, которую может иметь имя файла, зависит от ее абсолютного пути:

 (0 + 0 + 244 + 11 chars) C:\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\1234567.txt (0 + 3 + 240 + 11 chars) C:\123\123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1234567.txt (3 + 3 + 236 + 11 chars) C:\123\456\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1234567.txt 

Википедия говорит, что:

NTFS позволяет каждому компоненту пути (каталогу или имени файла) быть 255 символов.

Насколько я знаю (и тестирование), это неправильно.

Всего (сметы) все эти примеры имеют 259 символов, если вы разделите C:\ который дает 256 символов (не 255 ?!). Каталоги, созданные с помощью проводника, и вы заметите, что он сдерживает использование всего доступного пространства для имени каталога. Причина этого заключается в том, чтобы разрешить создание файлов с использованием соглашения об именах файлов 8.3 . То же самое происходит и для других разделов.

Файлы, конечно, не должны резервировать требования к длине 8.3:

 (255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt 

Вы не можете создавать дополнительные подкаталоги, если абсолютный путь родительского каталога имеет более 242 символов, потому что 256 = 242 + 1 + \ + 8 + . + 3 256 = 242 + 1 + \ + 8 + . + 3 . Используя проводник Windows, вы не можете создать другой каталог, если родительский каталог имеет более 233 символов (в зависимости от локали системы), так как 256 = 233 + 10 + \ + 8 + . + 3 256 = 233 + 10 + \ + 8 + . + 3 ; здесь 10 – длина строки New folder .

Файловая система Windows представляет собой неприятную проблему, если вы хотите обеспечить совместимость между файловыми системами.

3. Остерегайтесь зарезервированных символов и ключевых слов

Помимо удаления не-ASCII, непечатаемых и управляющих символов , вам также необходимо повторно (поместить / переместить):

 "*/:<>?\| 

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

  • "*? -> _
  • /\| -> -
  • : -> [ ]-[ ]
  • < -> (
  • > -> )

Есть также специальные ключевые слова, которых следует избегать (например, NUL ), хотя я не уверен, как это преодолеть. Возможно, черный список со случайным спадом имен будет хорошим подходом к его решению.

4. Чувствительность к регистру

Это должно быть My_File.txt , но если вы хотите, чтобы обеспечить уникальность файла в разных операционных системах, вы должны преобразовать имена файлов в нормализованный случай, таким образом, my_file.txt и My_File.txt в Linux не станут тем же файлом my_file.txt в Windows.

5. Убедитесь, что он уникален

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

Общие уникальные идентификаторы включают временную метку UNIX, дайджест содержимого файла или случайную строку.

6. Скрытые файлы

Просто потому, что его можно назвать, это не значит, что он должен …

Точки, как правило, имеют белый список в именах файлов, но в Linux скрытый файл представлен ведущей точкой.

7. Другие соображения

Если вам нужно удалить некоторые символы имени файла, расширение обычно более важно, чем базовое имя файла. Разрешив значительное максимальное количество символов для расширения файла (8-16), нужно удалить символы из базового имени. Также важно отметить, что в маловероятном случае, когда в этом случае должно быть более одного длинного расширения, например _.graphmlz.tag.gz_.graphmlz.tag _ следует рассматривать в качестве имени базы данных.

8. Ресурсы

Caliber обрабатывает имя файла mangling довольно прилично:

  • /src/calibre/utils/filenames.py
  • /src/calibre/library/save_to_disk.py

Страница Википедии об изменении имени файла и связанной с ним главе с помощью Samba .


Если, например, вы пытаетесь создать файл, который нарушает какое-либо из правил 1/2/3, вы получите очень полезную ошибку:

 Warning: touch(): Unable to create file ... because No error in ... on line ... 

Я всегда думал, что Кохана неплохо справился с этим .

 public static function title($title, $separator = '-', $ascii_only = FALSE) { if ($ascii_only === TRUE) { // Transliterate non-ASCII characters $title = UTF8::transliterate_to_ascii($title); // Remove all characters that are not the separator, az, 0-9, or whitespace $title = preg_replace('![^'.preg_quote($separator).'a-z0-9\s]+!', '', strtolower($title)); } else { // Remove all characters that are not the separator, letters, numbers, or whitespace $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', UTF8::strtolower($title)); } // Replace all separator characters and whitespace by a single separator $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title); // Trim separators from the beginning and end return trim($title, $separator); } 

Удобный UTF8::transliterate_to_ascii() превратит материал вроде ñ => n.

Конечно, вы могли бы заменить другие элементы UTF8::* функциями mb_ *.

Что касается загрузки файлов, вам было бы безопаснее запретить пользователю управлять именем файла. Как уже было намечено, сохраните canonicalized filename в базе данных вместе со случайно выбранным и уникальным именем, которое вы будете использовать в качестве фактического имени файла.

Используя OWASP ESAPI, эти имена могут быть сгенерированы таким образом:

 $userFilename = ESAPI::getEncoder()->canonicalize($input_string); $safeFilename = ESAPI::getRandomizer()->getRandomFilename(); 

Вы можете добавить временную метку в $ safeFilename, чтобы гарантировать, что случайно генерируемое имя файла уникально, даже не проверяя существующий файл.

Что касается кодировки URL и снова с помощью ESAPI:

 $safeForURL = ESAPI::getEncoder()->encodeForURL($input_string); 

Этот метод выполняет канонизацию перед кодированием строки и обрабатывает все кодировки символов.

Я адаптировался из другого источника и добавил пару лишних, возможно, немного переборщил

 /** * Convert a string into a url safe address. * * @param string $unformatted * @return string */ public function formatURL($unformatted) { $url = strtolower(trim($unformatted)); //replace accent characters, forien languages $search = array('À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć', 'ć', 'Ĉ', 'ĉ', 'Ċ', 'ċ', 'Č', 'č', 'Ď', 'ď', 'Đ', 'đ', 'Ē', 'ē', 'Ĕ', 'ĕ', 'Ė', 'ė', 'Ę', 'ę', 'Ě', 'ě', 'Ĝ', 'ĝ', 'Ğ', 'ğ', 'Ġ', 'ġ', 'Ģ', 'ģ', 'Ĥ', 'ĥ', 'Ħ', 'ħ', 'Ĩ', 'ĩ', 'Ī', 'ī', 'Ĭ', 'ĭ', 'Į', 'į', 'İ', 'ı', 'IJ', 'ij', 'Ĵ', 'ĵ', 'Ķ', 'ķ', 'Ĺ', 'ĺ', 'Ļ', 'ļ', 'Ľ', 'ľ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'Ń', 'ń', 'Ņ', 'ņ', 'Ň', 'ň', 'ʼn', 'Ō', 'ō', 'Ŏ', 'ŏ', 'Ő', 'ő', 'Œ', 'œ', 'Ŕ', 'ŕ', 'Ŗ', 'ŗ', 'Ř', 'ř', 'Ś', 'ś', 'Ŝ', 'ŝ', 'Ş', 'ş', 'Š', 'š', 'Ţ', 'ţ', 'Ť', 'ť', 'Ŧ', 'ŧ', 'Ũ', 'ũ', 'Ū', 'ū', 'Ŭ', 'ŭ', 'Ů', 'ů', 'Ű', 'ű', 'Ų', 'ų', 'Ŵ', 'ŵ', 'Ŷ', 'ŷ', 'Ÿ', 'Ź', 'ź', 'Ż', 'ż', 'Ž', 'ž', 'ſ', 'ƒ', 'Ơ', 'ơ', 'Ư', 'ư', 'Ǎ', 'ǎ', 'Ǐ', 'ǐ', 'Ǒ', 'ǒ', 'Ǔ', 'ǔ', 'Ǖ', 'ǖ', 'Ǘ', 'ǘ', 'Ǚ', 'ǚ', 'Ǜ', 'ǜ', 'Ǻ', 'ǻ', 'Ǽ', 'ǽ', 'Ǿ', 'ǿ'); $replace = array('A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 's', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'IJ', 'ij', 'J', 'j', 'K', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', 'l', 'l', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', 'O', 'o', 'O', 'o', 'O', 'o', 'OE', 'oe', 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's', 'f', 'O', 'o', 'U', 'u', 'A', 'a', 'I', 'i', 'O', 'o', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'A', 'a', 'AE', 'ae', 'O', 'o'); $url = str_replace($search, $replace, $url); //replace common characters $search = array('&', '£', '$'); $replace = array('and', 'pounds', 'dollars'); $url= str_replace($search, $replace, $url); // remove - for spaces and union characters $find = array(' ', '&', '\r\n', '\n', '+', ',', '//'); $url = str_replace($find, '-', $url); //delete and replace rest of special chars $find = array('/[^a-z0-9\-<>]/', '/[\-]+/', '/<[^>]*>/'); $replace = array('', '-', ''); $uri = preg_replace($find, $replace, $url); return $uri; } 

и это версия Joomla 3.3.2 от JFile::makeSafe($file)

 public static function makeSafe($file) { // Remove any trailing dots, as those aren't ever valid file names. $file = rtrim($file, '.'); $regex = array('#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#'); return trim(preg_replace($regex, '', $file)); } 

Я не думаю, что список символов для удаления безопасен. Я предпочел бы использовать следующее:

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

Для параметров url: используйте urlencode() для кодирования любых специальных символов.

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

Вот реализация CodeIgniter.

 /** * Sanitize Filename * * @param string $str Input file name * @param bool $relative_path Whether to preserve paths * @return string */ public function sanitize_filename($str, $relative_path = FALSE) { $bad = array( '../', '<!--', '-->', '<', '>', "'", '"', '&', '$', '#', '{', '}', '[', ']', '=', ';', '?', '%20', '%22', '%3c', // < '%253c', // < '%3e', // > '%0e', // > '%28', // ( '%29', // ) '%2528', // ( '%26', // & '%24', // $ '%3f', // ? '%3b', // ; '%3d' // = ); if ( ! $relative_path) { $bad[] = './'; $bad[] = '/'; } $str = remove_invisible_characters($str, FALSE); return stripslashes(str_replace($bad, '', $str)); } 

И зависимость remove_invisible_characters .

 function remove_invisible_characters($str, $url_encoded = TRUE) { $non_displayables = array(); // every control character except newline (dec 10), // carriage return (dec 13) and horizontal tab (dec 09) if ($url_encoded) { $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15 $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31 } $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 do { $str = preg_replace($non_displayables, '', $str, -1, $count); } while ($count); return $str; } 

Я рекомендую * URL для PHP (480+ звезд в Github) – «PHP-порт URLify.js из проекта Django. Транслитерация символов не-ascii для использования в URL-адресах».

Основное использование:

Чтобы создать пули для URL-адресов:

 <?php echo URLify::filter (' J\'étudie le français '); // "jetudie-le-francais" echo URLify::filter ('Lo siento, no hablo español.'); // "lo-siento-no-hablo-espanol" ?> 

Чтобы создать пули для имен файлов:

 <?php echo URLify::filter ('фото.jpg', 60, "", true); // "foto.jpg" ?> 

* Ни один из других предложений не соответствовал моим критериям:

  • Должна быть установлена ​​через композитора
  • Не следует зависеть от iconv, поскольку он ведет себя по-разному в разных системах
  • Должна быть расширяемой, чтобы допускать переопределения и пользовательские замены символов
  • Популярные (например, многие звезды в Гитубе)
  • Имеет тесты

В качестве бонуса URL-адрес также удаляет определенные слова и удаляет все символы, не транслитерированные.

Вот тестовый пример, когда тонны иностранных символов транслитерируются правильно, используя URL: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

Это хороший способ защитить имя загружаемого файла:

 $file_name = trim(basename(stripslashes($name)), ".\x00..\x20"); 

На этот вопрос уже есть несколько решений, но здесь я прочитал и протестировал большую часть кода, и я закончил с этим решением, которое представляет собой смесь того, что я узнал здесь:

Функция

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

Filesystem.php :

 <?php namespace COil\Bundle\COilCoreBundle\Component\HttpKernel\Util; use Symfony\Component\HttpKernel\Util\Filesystem as BaseFilesystem; /** * Extends the Symfony filesystem object. */ class Filesystem extends BaseFilesystem { /** * Make a filename safe to use in any function. (Accents, spaces, special chars...) * The iconv function must be activated. * * @param string $fileName The filename to sanitize (with or without extension) * @param string $defaultIfEmpty The default string returned for a non valid filename (only special chars or separators) * @param string $separator The default separator * @param boolean $lowerCase Tells if the string must converted to lower case * * @author COil <https://github.com/COil> * @see http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe * * @return string */ public function sanitizeFilename($fileName, $defaultIfEmpty = 'default', $separator = '_', $lowerCase = true) { // Gather file informations and store its extension $fileInfos = pathinfo($fileName); $fileExt = array_key_exists('extension', $fileInfos) ? '.'. strtolower($fileInfos['extension']) : ''; // Removes accents $fileName = @iconv('UTF-8', 'us-ascii//TRANSLIT', $fileInfos['filename']); // Removes all characters that are not separators, letters, numbers, dots or whitespaces $fileName = preg_replace("/[^ a-zA-Z". preg_quote($separator). "\d\.\s]/", '', $lowerCase ? strtolower($fileName) : $fileName); // Replaces all successive separators into a single one $fileName = preg_replace('!['. preg_quote($separator).'\s]+!u', $separator, $fileName); // Trim beginning and ending seperators $fileName = trim($fileName, $separator); // If empty use the default string if (empty($fileName)) { $fileName = $defaultIfEmpty; } return $fileName. $fileExt; } } 

Модульные тесты

Интересно то, что я создал тесты PHPUnit, прежде всего, чтобы проверить крайние случаи, и поэтому вы можете проверить, соответствует ли оно вашим потребностям: (Если вы обнаружите ошибку, не стесняйтесь добавить тестовый пример)

FilesystemTest.php :

 <?php namespace COil\Bundle\COilCoreBundle\Tests\Unit\Helper; use COil\Bundle\COilCoreBundle\Component\HttpKernel\Util\Filesystem; /** * Test the Filesystem custom class. */ class FilesystemTest extends \PHPUnit_Framework_TestCase { /** * test sanitizeFilename() */ public function testFilesystem() { $fs = new Filesystem(); $this->assertEquals('logo_orange.gif', $fs->sanitizeFilename('--logö _ __ ___ ora@@ñ--~gé--.gif'), '::sanitizeFilename() handles complex filename with specials chars'); $this->assertEquals('coilstack', $fs->sanitizeFilename('cOiLsTaCk'), '::sanitizeFilename() converts all characters to lower case'); $this->assertEquals('cOiLsTaCk', $fs->sanitizeFilename('cOiLsTaCk', 'default', '_', false), '::sanitizeFilename() lower case can be desactivated, passing false as the 4th argument'); $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil stack'), '::sanitizeFilename() convert a white space to a separator'); $this->assertEquals('coil-stack', $fs->sanitizeFilename('coil stack', 'default', '-'), '::sanitizeFilename() can use a different separator as the 3rd argument'); $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil stack'), '::sanitizeFilename() removes successive white spaces to a single separator'); $this->assertEquals('coil_stack', $fs->sanitizeFilename(' coil stack'), '::sanitizeFilename() removes spaces at the beginning of the string'); $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil stack '), '::sanitizeFilename() removes spaces at the end of the string'); $this->assertEquals('coilstack', $fs->sanitizeFilename('coil,,,,,,stack'), '::sanitizeFilename() removes non-ASCII characters'); $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil_stack '), '::sanitizeFilename() keeps separators'); $this->assertEquals('coil_stack', $fs->sanitizeFilename(' coil________stack'), '::sanitizeFilename() converts successive separators into a single one'); $this->assertEquals('coil_stack.gif', $fs->sanitizeFilename('cOil Stack.GiF'), '::sanitizeFilename() lower case filename and extension'); $this->assertEquals('copy_of_coil.stack.exe', $fs->sanitizeFilename('Copy of coil.stack.exe'), '::sanitizeFilename() keeps dots before the extension'); $this->assertEquals('default.doc', $fs->sanitizeFilename('____________.doc'), '::sanitizeFilename() returns a default file name if filename only contains special chars'); $this->assertEquals('default.docx', $fs->sanitizeFilename(' ___ - --_ __%%%%__¨¨¨***____ .docx'), '::sanitizeFilename() returns a default file name if filename only contains special chars'); $this->assertEquals('logo_edition_1314352521.jpg', $fs->sanitizeFilename('logo_edition_1314352521.jpg'), '::sanitizeFilename() returns the filename untouched if it does not need to be modified'); $userId = rand(1, 10); $this->assertEquals('user_doc_'. $userId. '.doc', $fs->sanitizeFilename('亐亐亐亐亐.doc', 'user_doc_'. $userId), '::sanitizeFilename() returns the default string (the 2nd argument) if it can\'t be sanitized'); } } 

The test results: (checked on Ubuntu with PHP 5.3.2 and MacOsX with PHP 5.3.17:

 All tests pass: phpunit -c app/ src/COil/Bundle/COilCoreBundle/Tests/Unit/Helper/FilesystemTest.php PHPUnit 3.6.10 by Sebastian Bergmann. Configuration read from /var/www/strangebuzz.com/app/phpunit.xml.dist . Time: 0 seconds, Memory: 5.75Mb OK (1 test, 17 assertions) 

I have entry titles with all kinds of weird latin characters as well as some HTML tags that I needed to translate into a useful dash-delimited filename format. I combined @SoLoGHoST's answer with a couple of items from @Xeoncross's answer and customized a bit.

  function sanitize($string,$force_lowercase=true) { //Clean up titles for filenames $clean = strip_tags($string); $clean = strtr($clean, array('Š' => 'S','Ž' => 'Z','š' => 's','ž' => 'z','Ÿ' => 'Y','À' => 'A','Á' => 'A','Â' => 'A','Ã' => 'A','Ä' => 'A','Å' => 'A','Ç' => 'C','È' => 'E','É' => 'E','Ê' => 'E','Ë' => 'E','Ì' => 'I','Í' => 'I','Î' => 'I','Ï' => 'I','Ñ' => 'N','Ò' => 'O','Ó' => 'O','Ô' => 'O','Õ' => 'O','Ö' => 'O','Ø' => 'O','Ù' => 'U','Ú' => 'U','Û' => 'U','Ü' => 'U','Ý' => 'Y','à' => 'a','á' => 'a','â' => 'a','ã' => 'a','ä' => 'a','å' => 'a','ç' => 'c','è' => 'e','é' => 'e','ê' => 'e','ë' => 'e','ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ñ' => 'n','ò' => 'o','ó' => 'o','ô' => 'o','õ' => 'o','ö' => 'o','ø' => 'o','ù' => 'u','ú' => 'u','û' => 'u','ü' => 'u','ý' => 'y','ÿ' => 'y')); $clean = strtr($clean, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u','—' => '-')); $clean = str_replace("--", "-", preg_replace("/[^a-z0-9-]/i", "", preg_replace(array('/\s/', '/[^\w-\.\-]/'), array('-', ''), $clean))); return ($force_lowercase) ? (function_exists('mb_strtolower')) ? mb_strtolower($clean, 'UTF-8') : strtolower($clean) : $clean; } 

I needed to manually add the em dash character (—) to the translation array. There may be others but so far my file names are looking good.

Так:

Part 1: My dad's “Žurburts”?—they're (not) the best!

будет выглядеть так:

part-1-my-dads-zurburts-theyre-not-the-best

I just add ".html" to the returned string.

why not simply use php's urlencode ? it replaces "dangerous" characters with their hex representation for urls (ie %20 for a space)

This post seems to work the best among all that I have tied. http://gsynuh.com/php-string-filename-url-safe/205

This is a good function:

 public function getFriendlyURL($string) { setlocale(LC_CTYPE, 'en_US.UTF8'); $string = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string); $string = preg_replace('~[^\-\pL\pN\s]+~u', '-', $string); $string = str_replace(' ', '-', $string); $string = trim($string, "-"); $string = strtolower($string); return $string; } 

Solution #1: You have ability to install PHP extensions on server (hosting)

For transliteration of "almost every single language on the planet Earth" to ASCII characters.

  1. Install PHP Intl extension first. This is command for Debian (Ubuntu): sudo aptitude install php5-intl

  2. This is my fileName function (create test.php and paste there following code):

 <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Test</title> </head> <body> <?php function pr($string) { print '<hr>'; print '"' . fileName($string) . '"'; print '<br>'; print '"' . $string . '"'; } function fileName($string) { // remove html tags $clean = strip_tags($string); // transliterate $clean = transliterator_transliterate('Any-Latin;Latin-ASCII;', $clean); // remove non-number and non-letter characters $clean = str_replace('--', '-', preg_replace('/[^a-z0-9-\_]/i', '', preg_replace(array( '/\s/', '/[^\w-\.\-]/' ), array( '_', '' ), $clean))); // replace '-' for '_' $clean = strtr($clean, array( '-' => '_' )); // remove double '__' $positionInString = stripos($clean, '__'); while ($positionInString !== false) { $clean = str_replace('__', '_', $clean); $positionInString = stripos($clean, '__'); } // remove '_' from the end and beginning of the string $clean = rtrim(ltrim($clean, '_'), '_'); // lowercase the string return strtolower($clean); } pr('_replace(\'~&([az]{1,2})(ac134/56f4315981743 8765475[]lt7ňl2ú5äňú138yé73ťž7ýľute|'); pr(htmlspecialchars('<script>alert(\'hacked\')</script>')); pr('Álix----_Ãxel!?!?'); pr('áéíóúÁÉÍÓÚ'); pr('üÿÄËÏÖÜ.ŸåÅ'); pr('nie4č aa§ôňäääaš'); pr('Мао Цзэдун'); pr('毛泽东'); pr('ماو تسي تونغ'); pr('مائو تسه‌تونگ'); pr('מאו דזה-דונג'); pr('მაო ძედუნი'); pr('Mao Trạch Đông'); pr('毛澤東'); pr('เหมา เจ๋อตง'); ?> </body> </html> 

This is the code used by Prestashop to sanitize urls :

 replaceAccentedChars 

is used by

 str2url 

to remove diacritics

 function replaceAccentedChars($str) { $patterns = array( /* Lowercase */ '/[\x{0105}\x{00E0}\x{00E1}\x{00E2}\x{00E3}\x{00E4}\x{00E5}]/u', '/[\x{00E7}\x{010D}\x{0107}]/u', '/[\x{010F}]/u', '/[\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{011B}\x{0119}]/u', '/[\x{00EC}\x{00ED}\x{00EE}\x{00EF}]/u', '/[\x{0142}\x{013E}\x{013A}]/u', '/[\x{00F1}\x{0148}]/u', '/[\x{00F2}\x{00F3}\x{00F4}\x{00F5}\x{00F6}\x{00F8}]/u', '/[\x{0159}\x{0155}]/u', '/[\x{015B}\x{0161}]/u', '/[\x{00DF}]/u', '/[\x{0165}]/u', '/[\x{00F9}\x{00FA}\x{00FB}\x{00FC}\x{016F}]/u', '/[\x{00FD}\x{00FF}]/u', '/[\x{017C}\x{017A}\x{017E}]/u', '/[\x{00E6}]/u', '/[\x{0153}]/u', /* Uppercase */ '/[\x{0104}\x{00C0}\x{00C1}\x{00C2}\x{00C3}\x{00C4}\x{00C5}]/u', '/[\x{00C7}\x{010C}\x{0106}]/u', '/[\x{010E}]/u', '/[\x{00C8}\x{00C9}\x{00CA}\x{00CB}\x{011A}\x{0118}]/u', '/[\x{0141}\x{013D}\x{0139}]/u', '/[\x{00D1}\x{0147}]/u', '/[\x{00D3}]/u', '/[\x{0158}\x{0154}]/u', '/[\x{015A}\x{0160}]/u', '/[\x{0164}]/u', '/[\x{00D9}\x{00DA}\x{00DB}\x{00DC}\x{016E}]/u', '/[\x{017B}\x{0179}\x{017D}]/u', '/[\x{00C6}]/u', '/[\x{0152}]/u'); $replacements = array( 'a', 'c', 'd', 'e', 'i', 'l', 'n', 'o', 'r', 's', 'ss', 't', 'u', 'y', 'z', 'ae', 'oe', 'A', 'C', 'D', 'E', 'L', 'N', 'O', 'R', 'S', 'T', 'U', 'Z', 'AE', 'OE' ); return preg_replace($patterns, $replacements, $str); } function str2url($str) { if (function_exists('mb_strtolower')) $str = mb_strtolower($str, 'utf-8'); $str = trim($str); if (!function_exists('mb_strtolower')) $str = replaceAccentedChars($str); // Remove all non-whitelist chars. $str = preg_replace('/[^a-zA-Z0-9\s\'\:\/\[\]-\pL]/u', '', $str); $str = preg_replace('/[\s\'\:\/\[\]-]+/', ' ', $str); $str = str_replace(array(' ', '/'), '-', $str); // If it was not possible to lowercase the string with mb_strtolower, we do it after the transformations. // This way we lose fewer special chars. if (!function_exists('mb_strtolower')) $str = strtolower($str); return $str; } 

There is 2 good answers to slugfy your data, use it https://stackoverflow.com/a/3987966/971619 or it https://stackoverflow.com/a/7610586/971619

 // CLEAN ILLEGAL CHARACTERS function clean_filename($source_file) { $search[] = " "; $search[] = "&"; $search[] = "$"; $search[] = ","; $search[] = "!"; $search[] = "@"; $search[] = "#"; $search[] = "^"; $search[] = "("; $search[] = ")"; $search[] = "+"; $search[] = "="; $search[] = "["; $search[] = "]"; $replace[] = "_"; $replace[] = "and"; $replace[] = "S"; $replace[] = "_"; $replace[] = ""; $replace[] = ""; $replace[] = ""; $replace[] = ""; $replace[] = ""; $replace[] = ""; $replace[] = ""; $replace[] = ""; $replace[] = ""; $replace[] = ""; return str_replace($search,$replace,$source_file); }