php - 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 파일과 같은 몇 가지 추가 문자를 허용합니다.

업데이트 : 유효한 사용을 생각할 수 없어 별표를 삭제했습니다.



Answers



솔루션에 대한 몇 가지 관찰 :

  1. 패턴의 끝에있는 'u'는 패턴 과 일치하는 텍스트가 UTF-8로 해석되지 않는다는 것을 의미합니다 (나는 당신이 후자라고 생각합니까?).
  2. \ w는 밑줄 문자와 일치합니다. URL에 포함시키지 않겠다는 가정하에 파일을 포함 시키십시오. 그러나 코드에는 URL에 밑줄이 포함될 수 있습니다.
  3. "foreign UTF-8"의 포함은 로케일에 따라 결정됩니다. 이것이 서버 또는 클라이언트의 로케일인지 여부는 분명하지 않습니다. PHP 문서에서 :

"단어"문자는 임의의 문자 또는 숫자 또는 밑줄 문자, 즉 Perl "단어"의 일부가 될 수있는 문자입니다. 문자 및 숫자의 정의는 PCRE의 문자표에 의해 제어되며 로케일 별 일치가 발생할 경우 달라질 수 있습니다. 예를 들어 "fr"(프랑스어) 로켈에서 128보다 큰 일부 문자 코드는 악센트 부호가있는 문자에 사용되며이 문자는 \ w와 일치합니다.

슬러그 만들기

기술적으로는 (URL 인코딩 규칙에 따라) 백분율로 인코딩해야하므로 추측 한 URL을 갖기 때문에 액센트 기호 등을 포스트 슬러그에 포함하면 안됩니다.

만약 내가 당신이라면, 소문자로 변환 한 후, 'special'문자를 동등한 문자 (예 : é -> e)로 변환하고 non - az 문자를 '-'로 바꾸고 단일 '-' 당신이 한 것처럼. 여기에 특수 문자를 변환하는 구현이 있습니다. https://web.archive.org/web/20130208144021/http://neo22s.com/slug

일반적으로 위생 처리

OWASP에는 Enterprise Security API의 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




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 년 9 월 업데이트

Alix Axel 은이 분야에서 놀라운 성과를 거두었습니다 . 그의 함수 프레임 워크에는 멋진 텍스트 필터와 변형이 포함되어 있습니다.




이것은 당신의 파일 이름을 안전하게해야합니다 ...

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

마지막 줄은.




이 시도:

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 Friendly Username in PHP?




이것은 솔루션을 제공하지는 않지만 (아직!) 정확한 답변이 아니지만 코멘트에 맞추기에는 너무 큽니다 ...

Windows 7 및 Ubuntu 12.04에서 몇 가지 테스트 (파일 이름 관련)를 수행했는데 알아 냈습니다.

1. PHP가 비 ASCII 파일 이름을 처리 할 수 ​​없습니다.

Windows와 Ubuntu가 유니 코드 파일 이름을 처리 할 수 ​​있지만 (PHP는 RTL 것조차도) PHP 5.3은 일반 old ISO-8859-1을 처리하기 위해 해킹이 필요하기 때문에 안전을 위해 ASCII 만 사용하는 것이 좋습니다.

2. 파일 이름의 길이 (특히 Windows)

우분투에서 파일명이 가질 수있는 최대 길이 (incluinding 확장자)는 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

위키 피 디아 (Wikipedia)

NTFS는 경로 구성 요소 (디렉토리 또는 파일 이름)가 255 자 길이가되도록합니다.

내가 아는 한 (그리고 테스트) 최선의 결과는 잘못된 것입니다.

C:\ 에서 256 문자 (255가 아닌)를 제거하면 합계 (슬래시 계산)의 모든 예제에 259 개의 문자가 포함됩니다. 탐색기를 사용하여 생성 된 디렉토리는 디렉토리 이름에 사용 가능한 공간을 모두 사용하지 못하도록합니다. 그 이유는 8.3 파일 명명 규칙을 사용하여 파일을 만들 수 있기 때문입니다. 다른 파티션에서도 마찬가지입니다.

파일은 물론 8.3 길이 요건을 예약 할 필요가 없습니다.

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

256 = 242 + 1 + \ + 8 + . + 3 이므로 상위 디렉토리의 절대 경로가 242자를 넘으면 하위 디렉토리를 더 이상 만들 수 없습니다 256 = 242 + 1 + \ + 8 + . + 3 256 = 242 + 1 + \ + 8 + . + 3 . 256 = 233 + 10 + \ + 8 + . + 3 이기 때문에 Windows 탐색기를 사용하면 상위 디렉토리에 233 자 (시스템로 I 일에 따라 다름)가있는 경우 다른 디렉토리를 작성할 수 없습니다 256 = 233 + 10 + \ + 8 + . + 3 256 = 233 + 10 + \ + 8 + . + 3 ; 여기서 10New folder 문자열의 길이입니다.

Windows 파일 시스템은 파일 시스템 간의 상호 운용성을 보장하려는 경우 문제가됩니다.

3. 예약 된 문자 및 키워드주의

비 ASCII, 인쇄 할 수없는 문자 및 제어 문자 를 제거하는 것 외에도 다음을 다시 수행 (배치 / 이동)해야합니다.

"*/:<>?\|

파일 이름이 일부 의미를 잃을 수 있기 때문에 이러한 문자를 제거하는 것이 가장 좋은 아이디어는 아닙니다. 나는 최소한이 문자들이 여러 번 나타나는 것은 하나의 밑줄 ( _ )로 대체해야한다고 생각합니다. 아니면 좀 더 대표적인 것 (이것은 단지 아이디어 일뿐입니다)이라고 생각합니다.

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

NUL 과 같이 피해야 할 특별한 키워드가 있지만이를 극복하는 방법은 잘 모르겠습니다. 아마도 무작위 이름 폴백 (fallback)을 가진 블랙리스트 (black list)가이를 해결하기위한 좋은 접근 방법 일 것입니다.

4. 대소 문자 구분

이것은 말할 것도없이 My_File.txt , 다른 운영 체제에서 파일의 고유성을 보장하려면 파일 이름을 정규화 된 경우로 변환해야합니다. My_File.txt Linux의 my_file.txtMy_File.txt 가 모두 동일한 my_file.txt 파일이되지는 않습니다 Windows에서.

5. 그것이 유일하다는 것을 확인하십시오.

파일 이름이 이미 존재하면 고유 식별자가 기본 파일 이름에 추가되어야합니다 .

공통 고유 식별자에는 UNIX 타임 스탬프, 파일 내용의 요약 또는 임의의 문자열이 포함됩니다.

6. 숨김 파일

그냥 그것이 지명 될 수 있기 때문에 그것이해야한다는 것을 의미하지는 않습니다 ...

점은 일반적으로 파일 이름에 흰색으로 나열되지만 Linux에서는 숨겨진 파일이 선행 점으로 표시됩니다.

7. 기타 고려 사항

파일 이름의 일부 char을 제거해야하는 경우 일반적으로 확장자는 파일의 기본 이름보다 중요합니다. 파일 확장자 (8-16)에 대해 상당한 최대 문자 수를 허용하면 기본 이름에서 문자를 제거해야합니다. _.graphmlz.tag.gz - _.graphmlz.tag 와 같이 하나 이상의 긴 확장자가있는 경우에는이 경우 파일베이스 이름으로 간주되어야 함을 유의해야합니다.

8. 자료

Calibre 는 꽤 괜찮은 파일 이름 mangling을 처리합니다 :

Wikipedia 페이지에서 mangling 을 사용하고 Using Samba에서 링크 된 장을 참조하십시오 .

예를 들어, 1/2/3 규칙 중 하나를 위반하는 파일을 만들려고하면 매우 유용한 오류가 발생합니다.

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



나는 항상 Kohana가 그것의 꽤 좋은 일을 했다고 생각 했습니다 .

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_ * 함수로 대체 할 수 있습니다.




파일 업로드 측면에서 사용자가 파일 이름을 제어하지 못하도록하는 것이 가장 안전합니다. 이미 암시 된 것처럼 실제 파일 이름으로 사용할 무작위로 선택된 고유 한 이름과 함께 데이터베이스에 정규 파일 이름을 저장하십시오.

OWASP ESAPI를 사용하면 다음과 같은 이름을 생성 할 수 있습니다.

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

$ safeFilename에 타임 스탬프를 추가하여 임의로 생성 된 파일 이름이 기존 파일을 확인하지 않고도 고유하도록 할 수 있습니다.

URL 인코딩 및 ESAPI 다시 사용 :

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

이 메소드는 캐릭터 라인을 encode하기 전에 정규화를 실행 해, 모든 문자 인코딩을 처리합니다.




나는 다른 소스에서 적응하고 몇 가지 추가, 어쩌면 조금 지나친 추가

/**
 * 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에서 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));
}



나는 제거 할 문자 목록을 갖는 것이 안전하다고 생각하지 않습니다. 차라리 다음을 사용합니다.

파일 이름 : 내부 ID 또는 파일 내용의 해시를 사용하십시오. 데이터베이스에 문서 이름을 저장하십시오. 이렇게하면 원래 파일 이름을 유지하면서 파일을 찾을 수 있습니다.

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



나는 * 추천 (Github에서에 480+ 별) PHP에 대한 URLify을 - "URLify.js의 PHP 포트를 장고 프로젝트의 URL에서 사용하기로 변환합니다 비 ASCII 문자를.".

기본 사용법 :

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에 의존하지해야
  • 재정 및 사용자 정의 문자 교체 할 수 있도록 확장해야한다
  • 인기 (Github에서의 예를 들어 많은 별)
  • 테스트를 가지고

보너스로, URLify 또한 특정 단어를 제거하지가 자역 모든 문자를 멀리 제거합니다.

여기 URLify를 사용하여 제대로 자역되는 외국 문자의 톤과 테스트 케이스는 다음과 같습니다 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://.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');
    }
}

테스트 결과 : (에서 확인할 우분투 PHP 5.3.2과 함께 에서라도 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)



나는 유용한 대시로 구분 된 파일 이름 형식으로 변환 할 필요가 이상한 라틴 문자의 모든 종류뿐만 아니라 일부 HTML 태그를 입력 종이 등록되어 있습니다. 나는 Xeoncross의 대답 @에서 항목의 부부와 함께 SoLoGHoST의 대답 @ 결합 및 비트 사용자 정의.

    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 부 : 아빠 "Žurburts"- 그들이있어 (안) 최고!

된다 :

파트 1 - 내 - 아빠 - zurburts-의 theyre -하지 - 더 - 최고

난 그냥 반환 된 문자열에 ".html 중에서"를 추가합니다.




이유는 간단 PHP는의를 사용하지 urlencode? 그것은 (즉 URL에 대한 자신의 진수 표현으로 "위험"문자 대체 %20공간을위한)을







이것은 좋은 기능입니다 :

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 : 서버에 PHP 확장을 설치할 수있는 능력을 가지고 (호스팅)

ASCII 문자로 "행성 지구에 거의 모든 단일 언어"의 음역하십시오.

  1. 설치 PHP 국제의 첫 번째 확장. 이것은 데비안 (우분투)에 대한 명령입니다 :sudo aptitude install php5-intl

  2. 이것은 내 fileName에 기능 (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 확장을 설치 할 수있는 능력이없는 (호스팅)

꽤 좋은 작업이 이루어집니다 음역 CMS 드루팔에 대한 모듈. 그것은 행성 지구에 거의 모든 단일 언어를 지원합니다. 나는 플러그인 확인하는 것이 좋습니다 저장소를 정말 완벽한 솔루션 살균 문자열을 갖고 싶어.




이 URL을 살균하는 PrestaShop 버전에서 사용되는 코드입니다 :

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



데이터를 slugfy하는이 개 좋은 답변 사용이 그것을 https://.com/a/3987966/971619 또는 https://.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);

}