ฆ่าเชื้อสตริงเพื่อทำให้ URL และชื่อไฟล์ปลอดภัยหรือไม่


136

ฉันพยายามหาฟังก์ชั่นที่ใช้งานได้ดีในการฆ่าเชื้อสตริงเพื่อให้พวกเขาปลอดภัยในการใช้ URL (เช่น post slug) และยังปลอดภัยที่จะใช้เป็นชื่อไฟล์ ตัวอย่างเช่นเมื่อมีคนอัปโหลดไฟล์ฉันต้องการให้แน่ใจว่าฉันลบตัวละครอันตรายทั้งหมดออกจากชื่อ

จนถึงตอนนี้ฉันได้พบกับฟังก์ชั่นต่อไปนี้ซึ่งฉันหวังว่าจะแก้ปัญหานี้และอนุญาตให้ใช้ข้อมูล 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

อัปเดต: นำตัวละครดาวออกเนื่องจากฉันไม่สามารถนึกถึงการใช้ที่ถูกต้อง


คุณควรลบทุกอย่างยกเว้น [\ w.-]
elias

3
คุณอาจพบว่าNormalizerและความคิดเห็นมีประโยชน์
Matt Gibson

คำตอบ:


57

ข้อสังเกตบางอย่างเกี่ยวกับวิธีแก้ปัญหาของคุณ:

  1. 'u' ที่ส่วนท้ายของรูปแบบของคุณหมายความว่ารูปแบบและไม่ใช่ข้อความที่การจับคู่จะถูกตีความเป็น UTF-8 (ฉันถือว่าคุณถือว่าเป็นรูปแบบหลังหรือไม่)
  2. \ w ตรงกับอักขระขีดล่าง คุณรวมไว้สำหรับไฟล์ที่นำไปสู่ข้อสันนิษฐานที่คุณไม่ต้องการใน URL แต่ในรหัสที่คุณมี URL จะได้รับอนุญาตให้ใส่เครื่องหมายขีดล่าง
  3. การรวม "UTF-8 ต่างประเทศ" ดูเหมือนจะขึ้นอยู่กับสถานที่เกิดเหตุ ไม่ชัดเจนว่านี่เป็นตำแหน่งที่ตั้งของเซิร์ฟเวอร์หรือไคลเอนต์ จากเอกสาร PHP:

อักขระ "คำ" คือตัวอักษรหรือตัวเลขหรือตัวอักษรขีดล่างซึ่งก็คือตัวอักษรใด ๆ ที่สามารถเป็นส่วนหนึ่งของคำว่า "Perl" คำจำกัดความของตัวอักษรและตัวเลขจะถูกควบคุมโดยตารางอักขระของ PCRE และอาจแตกต่างกันไปหากมีการจับคู่เฉพาะสถานที่เกิดขึ้น ตัวอย่างเช่นในโลแคล "fr" (ฝรั่งเศส) รหัสอักขระบางตัวที่มากกว่า 128 จะใช้สำหรับตัวอักษรที่เน้นเสียงและรหัสเหล่านี้จะถูกจับคู่โดย \ w

การสร้างกระสุน

คุณอาจไม่ควรใส่อักขระที่เน้นเสียงเป็นต้นในเทคนิคการลงรายการบัญชีเนื่องจากเทคนิคควรเป็นเปอร์เซ็นต์ที่เข้ารหัส (ตามกฎการเข้ารหัส URL) ดังนั้นคุณจะมี URL ที่ดูน่าเกลียด

ดังนั้นถ้าฉันเป็นคุณหลังจากลดระดับต่ำลงฉันจะแปลงอักขระ 'พิเศษ' ให้เป็นอักขระเทียบเท่า (เช่นé -> e) และแทนที่อักขระที่ไม่ใช่ [az] ด้วย '-' ซึ่ง จำกัด การทำงานเพียงตัวเดียว '-' อย่างที่คุณทำ มีการนำการแปลงอักขระพิเศษมาใช้ที่นี่: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

การฆ่าเชื้อโดยทั่วไป

OWASP มีการติดตั้ง PHP ของ Enterprise Security 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


คุณถูกต้องเกี่ยวกับการสันนิษฐานของฉันของการปรับปรุง "u" - ฉันคิดว่ามันเป็นข้อความ ฉันลืมเกี่ยวกับตัวปรับ \ w รวมถึงขีดล่างด้วย ปกติฉันจะแปลงอักขระที่เน้นเสียงทั้งหมดเป็น ASCII - แต่ฉันต้องการให้มันทำงานกับภาษาอื่นได้เช่นกัน ฉันสันนิษฐานว่าจะมีวิธีที่ปลอดภัย UTF-8 บางอย่างที่ตัวอักษรของภาษาใด ๆ สามารถใช้ในทาก URL หรือชื่อไฟล์เพื่อให้แม้แต่ชื่ออารบิกก็ใช้ได้ ท้ายที่สุดแล้ว linux รองรับชื่อไฟล์ UTF-8 และเบราว์เซอร์ควรเข้ารหัสลิงก์ HTML ตามต้องการ ขอบคุณมากสำหรับข้อมูลของคุณที่นี่
Xeoncross

ในความคิดที่สองคุณพูดถูก แต่ไม่ใช่แค่เบราว์เซอร์ที่เข้ารหัสลิงก์อย่างถูกต้อง วิธีที่ง่ายที่สุดในการเข้าถึงสิ่งที่คุณต้องการคือการจับคู่อักขระที่ไม่ใช่ ASCII กับ ASCII ที่ใกล้เคียงที่สุดจากนั้นเข้ารหัส URL ของคุณในส่วน HTML วิธีที่ยากคือการรับรองการเข้ารหัส UTF-8 ที่สอดคล้องกัน (หรือ UTF-16 ฉันคิดว่าเป็นภาษาจีนบางภาษา) จากแหล่งข้อมูลของคุณผ่านเว็บเซิร์ฟเวอร์ชั้นแอปพลิเคชัน (PHP) เนื้อหาหน้าเว็บเบราว์เซอร์และไม่ urlencode แต่ยังคงตัดตัวอักษร 'ที่ไม่พึงประสงค์' ไว้) สิ่งนี้จะทำให้คุณมีลิงค์และ URL ที่ไม่ได้เข้ารหัสดี
Alan Donnelly

คำปรึกษาที่ดี. ฉันจะพยายามสร้างสภาพแวดล้อม UTF-8 ที่บริสุทธิ์ จากนั้นรับสตริงจากภาษาที่ไม่ใช่ ASCII ฉันจะลบตัวอักษรอันตราย (./ ;: ฯลฯ ... ) และสร้างไฟล์แล้วลิงค์ HTML ไปยังไฟล์เหล่านั้นเพื่อดูว่าฉันสามารถคลิกได้หรือไม่และดูว่าทั้งหมดนี้ โรงงาน ถ้าไม่เช่นนั้นฉันอาจต้องย้อนกลับไปที่ (raw)? urlencode () เพื่ออนุญาต UTF-8 ฉันจะโพสต์ผลลัพธ์กลับมาที่นี่
Xeoncross

3
ฉันสร้างไฟล์ชื่อสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txtแล้วสร้างไฟล์ UTF-8 HTML พร้อมลิงก์ไปยังมัน มันใช้งานได้อย่างน่าอัศจรรย์แม้บนหน้าต่าง! อย่างไรก็ตามฉันมี PHP file_put_contents('สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt')และมันล้มเหลวในการสร้างชื่อไฟล์บาซ่าร์จากสตริงนั้น จากนั้นฉันก็ลองสร้างมันfopen()ขึ้นมาและสร้างชื่อไฟล์ที่ยุ่งเหยิง เห็นได้ชัดว่า PHP (บน windows เป็นอย่างน้อย) ไม่สามารถสร้างชื่อไฟล์ UTF-8 ได้ bugs.php.net/bug.php?id=46990&thanks=6
Xeoncross

1
ฉันให้รางวัลคำตอบนี้เพราะทำให้ฉันคิดมากที่สุดและรวมลิงค์ที่มีประโยชน์ไปยังโครงการที่ฉันไม่เคยได้ยินมาก่อนว่ามันคุ้มค่าที่จะดู ฉันจะโพสต์เมื่อฉันพบคำตอบว่า
Xeoncross

87

ฉันพบฟังก์ชันที่มีขนาดใหญ่กว่านี้ในรหัส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;
}

และอันนี้ในรหัสเวิร์ดเพรส

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

อัปเดตเมื่อกันยายน 2555

อลิกซ์แอ็กเซิลได้ทำงานที่น่าทึ่งในพื้นที่นี้ กรอบการทำงานของเขารวมถึงตัวกรองข้อความที่ยอดเยี่ยมและการเปลี่ยนแปลงหลายอย่าง


23
รหัส WordPress ไม่สามารถพกพาได้เนื่องจากมันใช้ประโยชน์apply_filters
Kevin Mark

1
โปรดทราบว่ารุ่น wordpress แทนที่/[\s-]+/ด้วย-ซึ่งดีกว่ารุ่นแรก (ซึ่งแทนที่เท่านั้น/\s+/) ที่สามารถทำให้เกิดเครื่องหมายหลายเส้นในแถว
Yotam Omer

เพียงสำหรับการอ้างอิง apply_filters WordPress สามารถพบได้ที่นี่และ sanitize_file_name กว่าที่นี่
Eric

แล้วมีหลายช่องว่างล่ะ แทนที่
Jeffrey the Giraffe

8
$ anal-Variable ฟังดูน่ากลัวมากสำหรับฉันพร้อมตัวเลือกการบังคับ
viljun

30

สิ่งนี้จะทำให้ชื่อไฟล์ของคุณปลอดภัย ...

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

สำหรับบรรทัดสุดท้าย


1
ยังขาดหายไปบางส่วนเช็กและสโลวักตัวอักษร:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet

22

ลองสิ่งนี้:

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?


ดีมาก - ฉันไม่เคยเห็นสิ่งนี้ทำโดยไม่มีตารางการแปล (เช่นใช้ wordpress) อย่างไรก็ตามฉันไม่คิดว่าฟังก์ชั่นนี้จะดีเท่าที่ควรเพราะมันแปลเฉพาะอักขระพิเศษ แต่ไม่ลบอักขระอันตราย อาจจะสามารถเพิ่มในข้างบน ...
Xeoncross

4
ฮา! การเข้ารหัสเอนทิตีแฮ็คนั้นหวานดี! แม้ว่ามันจะไม่ชัดเจนเลยในตอนแรกวิธีการนี้ทำในสิ่งที่มันทำ แม้ว่าจะมีปัญหา "Frédéric & Éric" จะไม่เปลี่ยนเป็น "Frederic amp Eric" หรือไม่?
Alan Donnelly

@AlanDonnelly: อันที่จริงผมได้ปรับปรุงฟังก์ชั่นในคำตอบเดิมของฉัน (ตรวจสอบการเชื่อมโยง) ที่ควรจะเป็นtrim() trim($string, '-')
Alix Axel

@ Xeoncross: สุดท้ายpreg_replace()ควรลบตัวอักษรอันตรายทั้งหมด
Alix Axel

@AlixAxel ทุกที่ของคุณไม่ใช่คุณ ฉันเพิ่งอ่าน PHP AWS SDK และพวกเขามีรหัสของคุณสำหรับ UUID รหัสที่น่ากลัวของ phunction นั้นยากที่จะเอาชนะ
Xeoncross

13

นี่ไม่ได้เป็นคำตอบที่แน่นอนเพราะมันยังไม่มีวิธีแก้ปัญหา (แต่!) แต่มันใหญ่เกินไปที่จะแสดงความคิดเห็น ...


ฉันทำการทดสอบบางอย่าง (เกี่ยวกับชื่อไฟล์) บน Windows 7 และ Ubuntu 12.04 และสิ่งที่ฉันพบคือ:

1. PHP ไม่สามารถจัดการกับชื่อไฟล์ที่ไม่ใช่ ASCII

แม้ว่าทั้ง Windows และ Ubuntu จะสามารถจัดการ Unicode ชื่อไฟล์ได้ (แม้จะเป็น RTL ก็ตาม) PHP 5.3 ต้องการแฮ็กที่จะจัดการกับ ISO-8859-1 แบบธรรมดาธรรมดาดังนั้นจึงควรเก็บ ASCII ไว้เพื่อความปลอดภัยเท่านั้น

2. ความยาวของเรื่องชื่อไฟล์ (พิเศษบน Windows)

บน Ubuntu ความยาวสูงสุดที่ชื่อไฟล์สามารถมี (ส่วนขยาย 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 ตัวอักษร

เท่าที่ฉันรู้ (และทดสอบ) สิ่งนี้ผิด

โดยรวม (การนับสแลช) ตัวอย่างทั้งหมดเหล่านี้มี 259 ตัวอักษรหากคุณตัดแถบC:\ที่มีอักขระ 256 ตัว (ไม่ใช่ 255 ?!) ไดเรกทอรีที่สร้างขึ้นโดยใช้ Explorer และคุณจะสังเกตได้ว่าไดเรกทอรีดังกล่าวขัดขวางไม่ให้ใช้พื้นที่ว่างทั้งหมดสำหรับชื่อไดเรกทอรี เหตุผลของเรื่องนี้คือการอนุญาตให้สร้างไฟล์โดยใช้การตั้งชื่อไฟล์ 8.3 สิ่งเดียวกันเกิดขึ้นสำหรับพาร์ติชันอื่น

ไฟล์ไม่จำเป็นต้องจองความต้องการ 8.3 ความยาวแน่นอน:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

คุณไม่สามารถสร้างเพิ่มเติมไดเรกทอรีย่อยใด ๆ หากเส้นทางที่แน่นอนของไดเรกทอรีแม่มีมากกว่า 242 256 = 242 + 1 + \ + 8 + . + 3ตัวอักษรเพราะ เมื่อใช้ Windows Explorer คุณจะไม่สามารถสร้างไดเรกทอรีอื่นได้หากไดเรกทอรีหลักมีอักขระมากกว่า 233 ตัว (ขึ้นอยู่กับตำแหน่งที่ตั้งของระบบ) เนื่องจาก256 = 233 + 10 + \ + 8 + . + 3; นี่คือความยาวของสตริง10New folder

ระบบไฟล์ Windows ก่อให้เกิดปัญหาที่น่ารังเกียจหากคุณต้องการรับประกันความสามารถในการทำงานร่วมกันระหว่างระบบไฟล์

3. ระวังอักขระที่สงวนไว้และคำหลัก

นอกเหนือจากการลบอักขระที่ไม่ใช่ ASCII ไม่ใช่พิมพ์ได้และควบคุมคุณยังต้อง (สถานที่ / ย้าย):

"*/:<>?\|

เพียงแค่ลบอักขระเหล่านี้ออกอาจไม่ใช่ความคิดที่ดีที่สุดเพราะชื่อไฟล์อาจสูญเสียความหมายบางอย่าง ฉันคิดว่าอย่างน้อยที่สุดสิ่งที่เกิดขึ้นหลาย ๆ ครั้งของตัวละครเหล่านี้ควรจะถูกแทนที่ด้วยขีดล่างเดียว ( _) หรือบางทีอาจจะเป็นตัวแทนมากกว่านี้ (นี่เป็นเพียงความคิด):

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

นอกจากนี้ยังมีคำหลักพิเศษที่ควรหลีกเลี่ยง (เช่นNUL) แม้ว่าฉันไม่แน่ใจว่าจะเอาชนะได้อย่างไร บางทีบัญชีดำที่มีชื่อสำรองแบบสุ่มอาจเป็นวิธีที่ดีในการแก้ไข

4. ความไวต่อกรณี

สิ่งนี้ควรดำเนินการโดยไม่บอก แต่ถ้าคุณต้องการให้มั่นใจถึงความเป็นเอกลักษณ์ของไฟล์ในระบบปฏิบัติการที่แตกต่างกันคุณควรแปลงชื่อไฟล์เป็นกรณีปกติวิธีการmy_file.txtและMy_File.txtบน Linux จะไม่กลายเป็นmy_file.txtไฟล์เดียวกันบน Windows

5. ตรวจสอบให้แน่ใจว่ามันไม่ซ้ำกัน

หากชื่อไฟล์มีอยู่แล้วตัวระบุที่ไม่ซ้ำกันควรจะถูกผนวกเข้ากับชื่อไฟล์พื้นฐาน

ตัวระบุที่ไม่ซ้ำกันทั่วไปรวมถึงการประทับเวลา UNIX การแยกเนื้อหาไฟล์หรือสตริงแบบสุ่ม

6. ไฟล์ที่ซ่อน

เพียงเพราะสามารถตั้งชื่อไม่ได้หมายความว่าควร ...

จุดมักจะเป็นสีขาวในชื่อไฟล์ แต่ใน Linux ไฟล์ที่ซ่อนจะแสดงด้วยจุดนำ

7. ข้อควรพิจารณาอื่น ๆ

หากคุณจำเป็นต้องตัดชื่อไฟล์บางไฟล์ส่วนขยายมักจะสำคัญกว่าชื่อฐานของไฟล์ การอนุญาตจำนวนอักขระสูงสุดสำหรับนามสกุลไฟล์ (8-16) ควรตัดอักขระจากชื่อฐาน นอกจากนี้ยังเป็นสิ่งสำคัญที่จะทราบว่าในกรณีที่ไม่น่าที่มีนามสกุลยาวมากกว่าหนึ่ง - เช่น_.graphmlz.tag.gz- _.graphmlz.tagเพียง_ควรจะถือว่าเป็นชื่อไฟล์ฐานในกรณีนี้

8. ทรัพยากร

ความสามารถในการจัดการชื่อไฟล์ mangling สวย decently:

หน้าวิกิพีเดียชื่อไฟล์ manglingและเชื่อมโยงบทจากการใช้แซมบ้า


ตัวอย่างเช่นหากคุณพยายามสร้างไฟล์ที่ละเมิดกฎข้อ 1/2/3 คุณจะได้รับข้อผิดพลาดที่มีประโยชน์มาก:

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

11

ฉันคิดเสมอว่าโคยาน่าจะทำงานได้ดีทีเดียว

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_ *


5

ในแง่ของการอัพโหลดไฟล์คุณจะปลอดภัยที่สุดเพื่อป้องกันผู้ใช้จากการควบคุมชื่อไฟล์ ดังที่มีการบอกใบ้ไว้แล้วให้เก็บชื่อไฟล์ canonicalised ในฐานข้อมูลพร้อมกับชื่อที่สุ่มเลือกและไม่ซ้ำกันซึ่งคุณจะใช้เป็นชื่อไฟล์จริง

การใช้ OWASP ESAPI สามารถสร้างชื่อเหล่านี้ได้:

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

คุณสามารถเพิ่มการประทับเวลาลงใน $ safeFilename เพื่อช่วยให้แน่ใจว่าชื่อไฟล์ที่สร้างแบบสุ่มนั้นไม่ซ้ำกันโดยไม่ต้องตรวจสอบไฟล์ที่มีอยู่

ในแง่ของการเข้ารหัสสำหรับ URL และอีกครั้งโดยใช้ ESAPI:

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

วิธีการนี้จะดำเนินการ canonicalisation ก่อนที่จะเข้ารหัสสตริงและจะจัดการการเข้ารหัสตัวละครทั้งหมด


แน่นอน - การควบคุมชื่อไฟล์ออกไปจากผู้ใช้จะป้องกันไม่ให้มีการอัพโหลด 2 ครั้งที่มีชื่อเหมือนกัน
CodeVirtuoso

5

ฉันแนะนำ * 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"

?>

* ไม่มีคำแนะนำอื่นใดที่ตรงกับเกณฑ์ของฉัน:

  • ควรติดตั้งผ่านผู้แต่ง
  • ไม่ควรขึ้นอยู่กับ iconv เนื่องจากจะทำงานแตกต่างกันไปตามระบบที่ต่างกัน
  • ควรขยายได้เพื่ออนุญาตการแทนที่และการแทนที่อักขระแบบกำหนดเอง
  • ได้รับความนิยม (เช่นดาวหลายดวงบน Github)
  • มีการทดสอบ

ในฐานะโบนัส URLify ยังลบคำบางคำและลบอักขระทั้งหมดที่ไม่ถูกถอดเสียงออก

ต่อไปนี้เป็นกรณีทดสอบที่มีตัวอักษรต่างประเทศจำนวนมากที่ถูกทับศัพท์อย่างถูกต้องโดยใช้ URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f


1
ขอบคุณ - มันดูเหมาะสำหรับจุดประสงค์ของฉัน
David Goodwin

5

ฉันได้ดัดแปลงมาจากแหล่งอื่นและเพิ่มพิเศษอีกสองสามครั้ง

/**
 * 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

และนี่คือ 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));
}

4

ฉันไม่คิดว่าจะมีรายการตัวอักษรที่ลบได้อย่างปลอดภัย ฉันอยากจะใช้สิ่งต่อไปนี้:

สำหรับชื่อไฟล์: ใช้ ID ภายในหรือแฮชของไฟล์เนื้อหา บันทึกชื่อเอกสารในฐานข้อมูล วิธีนี้คุณสามารถเก็บชื่อไฟล์ดั้งเดิมและยังหาไฟล์ได้

สำหรับพารามิเตอร์ url: ใช้urlencode()เพื่อเข้ารหัสอักขระพิเศษใด ๆ


1
ฉันเห็นด้วยวิธีการส่วนใหญ่ในรายการที่นี่ลบตัวละครอันตรายที่รู้จัก - วิธีการของฉันลบทุกอย่างที่ไม่ได้เป็นตัวละครที่ปลอดภัย เนื่องจากระบบส่วนใหญ่บุ้งเข้ารหัส URL ที่โพสต์ของฉันขอแนะนำให้เราดำเนินการตามวิธีการที่พิสูจน์แล้วนี้แทนการใช้urlencode ที่ไม่ปลอดภัย UTF-8 ที่ทำเป็นเอกสาร
Xeoncross

3

ขึ้นอยู่กับว่าคุณจะใช้มันอย่างไรคุณอาจต้องการเพิ่มขีดจำกัดความยาวเพื่อป้องกันบัฟเฟอร์ล้น


ใช่การทดสอบ mb_strlen () เป็นสิ่งสำคัญเสมอ!
Xeoncross

3

นี่เป็นวิธีที่ดีในการรักษาความปลอดภัยชื่อไฟล์อัปโหลด:

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

ผมไม่แน่ใจว่าเกี่ยวกับเรื่องนี้อย่างใดอย่างหนึ่งสามารถลดลงได้.\x00..\x20 .\x00\x20
Xeoncross

@ Xeoncross: ฉันคิดว่า.\x00..\x20จะลบจุดและตัวละครทุกตัวระหว่าง\x00และ\x20ในขณะที่.\x00\x20ควรลบ 3 ไบต์เหล่านั้นเท่านั้น
Alix Axel

คำตอบนี้ต้องการคำอธิบายเพิ่มเติมเพื่อให้สามารถใช้ได้อย่างปลอดภัย มีข้อมูลไม่มากเกี่ยวกับไวยากรณ์ที่ถูกต้องสำหรับ charlist บนเน็ต
มานูเอลอาร์เวดชมิดท์

3

นี่คือการใช้งาน 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;
}

2

ทำไมไม่ใช้ php เพียงอย่างเดียวurlencode? มันแทนที่อักขระ "อันตราย" ด้วยการแสดงเลขฐานสิบหกของพวกเขาสำหรับ URL (เช่น%20สำหรับช่องว่าง)


2
ไม่แนะนำให้ใช้อักขระ% สำหรับชื่อไฟล์และอักขระที่เข้ารหัสฐานสิบหกใน URL เบราว์เซอร์สามารถรองรับสตริง UTF-8 ซึ่งดีกว่าและง่ายกว่าสำหรับภาษาที่ไม่ใช่ ASCII
Xeoncross

คุณสามารถทำ urlencode และจากนั้น str_replace ('% 20', '-', url)
Francesco

2

มีวิธีแก้ปัญหาหลายข้อสำหรับคำถามนี้แล้ว แต่ฉันได้อ่านและทดสอบรหัสส่วนใหญ่ที่นี่แล้วและฉันก็ลงเอยด้วยวิธีแก้ปัญหานี้ซึ่งเป็นการผสมผสานระหว่างสิ่งที่ฉันเรียนรู้ที่นี่:

ฟังก์ชั่น

ฟังก์ชั่นนี้รวมอยู่ที่นี่ในชุด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)

1
สิ่งนี้ถือว่าอินพุตส่วนใหญ่เป็นภาษาละติน เพิ่ม UTF-8 เพิ่มเติมจากภาษาอื่น ๆ เพื่อดูว่าคุณจะมีปัญหาที่ไหน
Xeoncross

@ Xeoncross ฉันเห็นด้วยอย่างที่คริสเตียนบอกไว้ว่าต้องบันทึก ID หรือแฮชและชื่อไฟล์ดั้งเดิม แต่ฟังก์ชั่นนี้มีทางเลือกอื่นเนื่องจากคุณสามารถระบุสตริงเริ่มต้นเมื่อกระบวนการฆ่าเชื้อล้มเหลว ฉันได้เพิ่มการทดสอบหน่วยสำหรับกรณีนี้ ขอบคุณสำหรับการรายงานข้อผิดพลาด
COil

2

ฉันมีชื่อรายการที่มีตัวอักษรละตินแปลก ๆ ทุกชนิดรวมถึงแท็ก 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;
}

ฉันต้องการเพิ่มตัวอักษรขีดประ (-) ไปยังอาร์เรย์การแปลด้วยตนเอง อาจมีคนอื่น ๆ แต่จนถึงขณะนี้ชื่อไฟล์ของฉันดูดี

ดังนั้น:

ส่วนที่ 1: พ่อของฉัน“ ururburts”? - พวกเขา (ไม่) ดีที่สุด!

กลายเป็น:

ส่วน-1-My-พ่อ-zurburts-พวกเขาจะไม่ได้ที่ดีที่สุด

ฉันเพิ่งเพิ่ม ".html" ลงในสตริงที่ส่งคืน


1
ยังขาดหายไปบางส่วนเช็กและสโลวักตัวอักษร:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet

1
และไม่ต้องสงสัยอีกมากมาย จริง ๆ แล้วฉันพยายามที่จะคิดออกว่ามีชุด ISO ที่มีตัวอักษรผสมกันหรือไม่ ชุด "เลือก" หนึ่งชุดจะทำอย่างไรถ้าเนื้อหานั้นต้องการตัวละครจากทั้งหมด UTF-8 ฉันสมมติว่า ...
cbmtrx

ฉันพบวิธีการแปลสตริงโดยใช้หนึ่งบรรทัดของ PHP : $string = transliterator_transliterate('Any-Latin;Latin-ASCII;', $string);ดูคำตอบของฉันด้านล่างหรืออ่านโพสต์บล็อกที่เชื่อมโยง
Jasom Dotnet

1
ไม่มีคุณต้องอ่านมันผิด: ถ้าคุณสามารถติดตั้งส่วนขยาย PHP บนเซิร์ฟเวอร์ของคุณ (หรือโฮสติ้ง) :-) นี่คือการโพสต์
Jasom Dotnet

1
อ่าเข้าใจแล้ว ขอบคุณ @JasomDotnet - ฉันมีวิธีแก้ปัญหาปัจจุบันที่ใช้งานได้ในตอนนี้ แต่เป็นชุดอักขระที่ จำกัด ดังนั้นส่วนขยายจึงคุ้มค่าที่จะเช็คเอาท์
cbmtrx

2

โซลูชัน # 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 บนเซิร์ฟเวอร์ (โฮสต์)

ป้อนคำอธิบายรูปภาพที่นี่

งานที่ค่อนข้างดีทำในโมดูลการถอดเสียงสำหรับ CMS Drupal รองรับภาษาเกือบทุกภาษาบนโลก ฉันขอแนะนำให้ตรวจสอบที่เก็บปลั๊กอินถ้าคุณต้องการให้มีการฆ่าเชื้อสตริงที่สมบูรณ์



1

นี่เป็นฟังก์ชั่นที่ดี:

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

มันดูไม่ดี \\s+หมายถึงแบ็กสแลชตามด้วยช่องว่างหนึ่งช่องขึ้นไป เกี่ยวกับอะไร นอกจากนี้การใช้งานนี้ขึ้นบัญชีดำมากกว่ายกเว้นสิ่งที่ละเลยชอบโมฆะหรือCMD BEL
Xeoncross

ยังไม่ดี /blog/2014-02/just-in-timeไม่อนุญาตให้ใช้สตริงเช่นนี้ กรุณาใช้รหัสทดสอบด้านบนหรือใช้phunctionรหัสกรอบ PHP
Xeoncross

ถูกตัอง. ฟังก์ชั่นนี้ใช้สำหรับส่วน "ทันเวลา" เท่านั้น อาจมีประโยชน์สำหรับบางคน
joan16v

1
คุณสามารถเปลี่ยน regexpreg_replace('~[^\-\pL\pN\s]+~u', '-', $string)
Xeoncross

! น่ากลัว ฉันยังเพิ่ม: string = trim ($ string, "-");
joan16v

0

นี่คือรหัสที่ใช้โดย 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;
}


-4
// 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);

} 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.