Очистка строк, чтобы сделать их 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 позволяет использовать некоторые дополнительные символы, такие как временные vim-файлы

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

Author: Lightness Races in Orbit, 2010-04-19

23 answers

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

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

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

Создание слизняка

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

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

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

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

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

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

 56
Author: Alan Donnelly, 2015-11-10 17:43:33

Я нашел эту более крупную функцию в коде 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("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
                   "}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—",
                   "—", "–", ",", "<", ".", ">", "/", "?");
    $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 года

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

 85
Author: Xeoncross, 2017-05-23 12:10:12

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

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

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

$clean_name = strtolower($clean_name);

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

 29
Author: SoLoGHoST, 2014-08-13 03:25:51

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

function normal_chars($string)
{
    $string = htmlentities($string, ENT_QUOTES, 'UTF-8');
    $string = preg_replace('~&([a-z]{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 в PHP?

 21
Author: John Conde, 2017-05-23 12:17:25

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


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

1. PHP Не Может Обрабатывать Имена Файлов, отличные от ASCII

Хотя и Windows, и Ubuntu могут обрабатывать имена файлов в Юникоде (даже 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. Используя проводник Windows, вы не можете создать другой каталог, если родительский каталог содержит более 233 символов (в зависимости от языкового стандарта системы), потому что 256 = 233 + 10 + \ + 8 + . + 3; 10 здесь длина строка New folder.

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

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

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

"*/:<>?\|

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

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

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

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

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

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

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

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

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

То, что он может быть назван, не означает, что он должен...

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

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

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

8. Ресурсы

Калибр довольно прилично обрабатывает искажение имени файла:

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


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

Warning: touch(): Unable to create file ... because No error in ... on line ...
 13
Author: Alix Axel, 2012-10-11 19:15:42

Я всегда думал, что Кохана проделала довольно хорошую работу.

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, a-z, 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_*.

 11
Author: alex, 2011-09-14 01:18:50

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

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

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

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

С точки зрения кодирования URL-адреса и снова с использованием ESAPI:

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

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

 5
Author: jah, 2010-04-25 20:47:26

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

/**
 * 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;
}
 5
Author: John Magnolia, 2016-10-13 13:14:15

И это версия 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));
}
 5
Author: cedric.walter, 2017-03-12 13:45:48

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

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

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

 4
Author: ZeissS, 2011-10-23 00:56:12

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

 3
Author: Tgr, 2010-04-25 19:59:37

Вот реализация 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;
}
 3
Author: Kevin Mark, 2012-11-05 03:41:01

Я рекомендую*URLIFY для 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"

?>

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

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

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

Вот тестовый пример с тоннами иностранных символов, правильно транслитерированных с помощью URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

 3
Author: Motin, 2016-07-18 09:30:15

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

$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
 2
Author: games, 2012-09-30 00:29:53

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

Функция

Функция включена здесь в пакет 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');
    }
}

Результаты теста: (проверено на Ubuntu с PHP 5.3.2 и MacOSX с 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)
 2
Author: COil, 2012-12-08 12:49:27

У меня есть заголовки записей со всевозможными странными латинскими символами, а также некоторые HTML-теги, которые мне нужно было перевести в полезный формат имени файла с разделителями в виде тире. Я объединил ответ @Sologhost с несколькими пунктами из ответа @Xeoncross и немного изменил его.

    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;
}

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

Итак:

Часть 1: "Журбурц" моего отца?- они (не) лучший!

Становится:

Часть 1-мои-папы-зурбурты-они-не-лучшие

Я просто добавляю ".html" в возвращаемую строку.

 2
Author: cbmtrx, 2015-12-08 19:02:51

Почему бы просто не использовать php urlencode? он заменяет "опасные" символы их шестнадцатеричным представлением для URL-адресов (т.Е. %20 для пробела)

 1
Author: knittl, 2010-04-27 17:05:42

Этот пост, кажется, работает лучше всего из всех, что я связал. http://gsynuh.com/php-string-filename-url-safe/205

 1
Author: ktamlyn, 2012-12-20 02:49:32

Это хорошая функция:

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;
} 
 1
Author: joan16v, 2015-01-15 08:16:48

Решение №1: У вас есть возможность устанавливать расширения PHP на сервере (хостинге)

Для транслитерации "почти каждого отдельного языка на планете Земля" в символы ASCII.

  1. Сначала установите расширение PHP Intl. Это команда для Debian (Ubuntu): sudo aptitude install php5-intl

  2. Это моя функция имени файла (создать test.php и вставьте туда следующий код):

<!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(\'~&([a-z]{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č a a§ôňäääaš');
pr('Мао Цзэдун');
pr('毛泽东');
pr('ماو تسي تونغ');
pr('مائو تسه‌تونگ');
pr('מאו דזה-דונג');
pr('მაო ძედუნი');
pr('Mao Trạch Đông');
pr('毛澤東');
pr('เหมา เจ๋อตง');
?>
</body>
</html>

Эта строка ядро:

  // transliterate
  $clean = transliterator_transliterate('Any-Latin;Latin-ASCII;', $clean);

Ответ, основанный на этом сообщении.

Решение №2: У вас нет возможности устанавливать расширения PHP на сервере (хостинге)

enter image description here

Довольно хорошая работа проделана в модуле транслитерации для CMS Drupal. Он поддерживает почти все языки на планете Земля. Я предлагаю проверить плагин репозиторий, если вы хотите иметь действительно полное решение для очистки строк.

 1
Author: Jasom Dotnet, 2015-12-15 14:15:16

Это код, используемый Prestashop для очистки URL-адресов:

replaceAccentedChars

Используется

str2url

Для удаления диакритических знаков

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;
}
 0
Author: Armel Larcier, 2013-06-19 08:36:46

Есть 2 хороших ответа для обработки ваших данных, используйте их https://stackoverflow.com/a/3987966/971619 или это https://stackoverflow.com/a/7610586/971619

 0
Author: newway, 2017-05-23 11:46:21
// 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);

} 
 -4
Author: Brant Messenger, 2010-04-19 16:04:08