Vệ sinh chuỗi để làm cho chúng URL và tên tệp an toàn?


136

Tôi đang cố gắng đưa ra một chức năng làm tốt công việc vệ sinh một số chuỗi nhất định để chúng an toàn khi sử dụng trong URL (như sên bài) và cũng an toàn để sử dụng làm tên tệp. Ví dụ: khi ai đó tải lên một tệp tôi muốn đảm bảo rằng tôi xóa tất cả các ký tự nguy hiểm khỏi tên.

Cho đến nay tôi đã đưa ra chức năng sau đây mà tôi hy vọng sẽ giải quyết vấn đề này và cũng cho phép dữ liệu UTF-8 nước ngoài.

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

Có ai có bất kỳ dữ liệu mẫu khó khăn nào tôi có thể chạy theo điều này - hoặc biết một cách tốt hơn để bảo vệ các ứng dụng của chúng tôi khỏi các tên xấu?

$ is-filename cho phép một số ký tự bổ sung như tệp temp vim

Cập nhật: đã xóa ký tự sao vì tôi không thể nghĩ đến việc sử dụng hợp lệ


Tốt hơn hết, bạn nên xóa mọi thứ trừ [\ w.-]
elias

3
Bạn có thể thấy Trình chuẩn hóa và các nhận xét về nó hữu ích.
Matt Gibson

Câu trả lời:


57

Một số quan sát về giải pháp của bạn:

  1. 'u' ở phần cuối của phương tiện mô hình của bạn mà người mẫu , và không phải là văn bản của nó phù hợp sẽ được hiểu là UTF-8 (Tôi đoán bạn giả định sau này?).
  2. \ w khớp với ký tự gạch dưới. Bạn đặc biệt bao gồm nó cho các tệp dẫn đến giả định rằng bạn không muốn chúng trong URL, nhưng trong mã bạn có URL sẽ được phép bao gồm dấu gạch dưới.
  3. Việc đưa vào "UTF-8 nước ngoài" dường như phụ thuộc vào địa phương. Không rõ đây là miền địa phương của máy chủ hay máy khách. Từ các tài liệu PHP:

Ký tự "từ" là bất kỳ chữ cái hoặc chữ số hoặc ký tự gạch dưới, nghĩa là, bất kỳ ký tự nào có thể là một phần của "từ" Perl. Định nghĩa của các chữ cái và chữ số được điều khiển bởi các bảng ký tự của PCRE và có thể thay đổi nếu diễn ra kết hợp cụ thể theo địa phương. Ví dụ: trong ngôn ngữ "fr" (tiếng Pháp), một số mã ký tự lớn hơn 128 được sử dụng cho các chữ cái có dấu và chúng được khớp với \ w.

Tạo sên

Bạn có thể không nên bao gồm các ký tự có dấu, v.v. trong sên bài đăng của mình vì về mặt kỹ thuật, chúng phải được mã hóa phần trăm (theo quy tắc mã hóa URL) để bạn có URL trông xấu xí.

Vì vậy, nếu tôi là bạn, sau khi hạ cấp, tôi sẽ chuyển đổi bất kỳ ký tự 'đặc biệt' nào thành tương đương (ví dụ é -> e) và thay thế các ký tự không [az] bằng '-', giới hạn chạy các ký tự '-' như bạn đã làm Có một triển khai chuyển đổi các ký tự đặc biệt ở đây: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Vệ sinh nói chung

OWASP có triển khai PHP API bảo mật doanh nghiệp của họ, trong số những thứ khác bao gồm các phương thức mã hóa và giải mã đầu vào và đầu ra an toàn trong ứng dụng của bạn.

Giao diện Encoder cung cấp:

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/Carget:OWASP_ Entryprise_Security_API


Bạn đã đúng về giả định của tôi về công cụ sửa đổi "u" - tôi nghĩ rằng nó là dành cho văn bản. Tôi cũng quên mất công cụ sửa đổi \ w bao gồm cả dấu gạch dưới. Tôi thường sẽ chuyển đổi tất cả các ký tự có dấu sang ASCII - nhưng tôi cũng muốn nó hoạt động với các ngôn ngữ khác. Tôi đã giả định rằng sẽ có một số cách an toàn UTF-8 mà bất kỳ ký tự ngôn ngữ nào cũng có thể được sử dụng trong một sên URL hoặc tên tệp để ngay cả các tiêu đề tiếng Ả Rập cũng hoạt động. Xét cho cùng, linux hỗ trợ tên tệp UTF-8 và trình duyệt sẽ mã hóa các liên kết HTML khi cần. Cảm ơn rất nhiều cho đầu vào của bạn ở đây.
Xeoncross

Suy nghĩ thứ hai, bạn thực sự đúng, nhưng đó không chỉ là vấn đề với trình duyệt mã hóa các liên kết một cách chính xác. Cách dễ nhất để đạt được gần với những gì bạn muốn là ánh xạ các ký tự không phải ASCII sang tương đương ASCII gần nhất của chúng và sau đó mã hóa URL liên kết của bạn trong phần thân HTML. Cách khó là đảm bảo mã hóa UTF-8 nhất quán (hoặc UTF-16, tôi nghĩ đối với một số phương ngữ Trung Quốc) từ cửa hàng dữ liệu của bạn, thông qua máy chủ web, lớp ứng dụng (PHP), nội dung trang, trình duyệt web và không url url của bạn ( nhưng vẫn lột bỏ những ký tự 'không mong muốn'). Điều này sẽ cung cấp cho bạn các liên kết và URL không được mã hóa đẹp.
Alan Donnelly

Lời khuyên tốt. Tôi sẽ cố gắng tạo ra một môi trường UTF-8 thuần túy. Sau đó, lấy một số chuỗi từ các ngôn ngữ không phải ASCII, tôi sẽ xóa các ký tự nguy hiểm (./ ;: vv ...) và tạo các tệp và sau đó liên kết HTML đến các tệp đó để xem liệu tôi có thể nhấp vào chúng không và xem liệu tất cả có làm. Nếu không thì có lẽ tôi sẽ phải quay trở lại (raw)? Urlencode () để cho phép UTF-8. Tôi sẽ gửi lại kết quả ở đây.
Xeoncross

3
Tôi đã tạo một tệp được gọi สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txtvà sau đó tạo tệp HTML UTF-8 có liên kết đến tệp đó. Thật đáng ngạc nhiên nó đã làm việc - ngay cả trên các cửa sổ! Tuy nhiên, sau đó tôi đã có PHP file_put_contents('สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt')và nó đã thất bại trong việc tạo tên tệp bazaar từ chuỗi đó. Sau đó, tôi đã cố gắng tạo nó với fopen()và có cùng tên tệp. Vì vậy, rõ ràng PHP (ít nhất là trên windows) không có khả năng tạo tên tệp UTF-8. bug.php.net/orms.php?id=46990&thanks=6
Xeoncross

1
Tôi trao giải cho câu trả lời này bởi vì nó khiến tôi suy nghĩ nhiều nhất và cũng bao gồm một liên kết hữu ích cho một dự án mà tôi chưa bao giờ nghe về điều đó đáng để xem xét. Tôi sẽ đăng một khi tôi tìm thấy một câu trả lời mặc dù.
Xeoncross

87

Tôi tìm thấy hàm lớn hơn này trong mã 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;
}

và cái này trong mã 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);
}

Cập nhật tháng 9 năm 2012

Alix Axel đã thực hiện một số công việc đáng kinh ngạc trong lĩnh vực này. Khung chức năng của anh ấy bao gồm một số bộ lọc văn bản tuyệt vời và biến đổi.


23
Mã WordPress không di động vì nó sử dụngapply_filters
Kevin Mark

1
Lưu ý rằng các thay thế phiên bản wordpress /[\s-]+/với -đó là tốt hơn so với phiên bản đầu tiên (mà chỉ thay thế /\s+/) có thể gây ra nhiều dấu gạch ngang liên tiếp
Yotam Omer

Chỉ để tham khảo wordpress application_filters có thể được tìm thấy ở đây và sanitize_file_name ở đây .
Eric

Còn nhiều không gian thì sao? Thay thế
Jeffrey the Gi hươu cao cổ

8
$ Hậu môn có thể thay đổi âm thanh rất đáng sợ đối với tôi với tùy chọn bắt buộc.
viljun

30

Điều này sẽ làm cho tên tệp của bạn an toàn ...

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

và một giải pháp sâu sắc hơn cho vấn đề này là:

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

Điều này giả định rằng bạn muốn một dấu chấm trong tên tệp. nếu bạn muốn nó được chuyển sang chữ thường, chỉ cần sử dụng

$clean_name = strtolower($clean_name);

cho dòng cuối cùng.


1
Vẫn còn thiếu một số nhân vật Séc và Slovakia:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet

22

Thử cái này:

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

Dựa trên câu trả lời được chọn trong chuỗi này: Tên người dùng thân thiện với URL trong PHP?


Rất hay - Tôi chưa bao giờ thấy điều này được thực hiện mà không có bảng dịch (như sử dụng wordpress). Tuy nhiên, tôi không nghĩ chức năng này là đủ vì nó chỉ dịch các ký tự đặc biệt nhưng không loại bỏ các ký tự nguy hiểm. Có lẽ nó có thể được thêm vào một ở trên ...
Xeoncross

4
Hà! Đó là hack mã hóa thực thể là ngọt ngào! Mặc dù thoạt nhìn không rõ ràng về phương pháp này làm như thế nào. Có một vấn đề mặc dù. "Frédéric & Éric" có biến thành "Frederic amp Eric" không?
Alan Donnelly

@AlanDonnelly: Thật vậy, tôi đã cập nhật chức năng trong câu trả lời ban đầu của mình (kiểm tra liên kết), trim()cũng nên như vậy trim($string, '-').
Axe Alix

@Xeoncross: Việc cuối cùng preg_replace()nên loại bỏ tất cả các ký tự nguy hiểm.
Alix Axel

@AlixAxel, bạn chỉ ở mọi nơi không phải là bạn. Tôi mới đọc qua SDK AWS của PHP và họ đã có một số mã của bạn cho UUID. Mã tuyệt vời của chức năng là khó đánh bại.
Xeoncross

13

Đây không phải là một câu trả lời chính xác vì nó không cung cấp bất kỳ giải pháp nào (chưa!), Nhưng nó quá lớn để phù hợp với một nhận xét ...


Tôi đã thực hiện một số thử nghiệm (liên quan đến tên tệp) trên Windows 7 và Ubuntu 12.04 và điều tôi phát hiện ra là:

1. PHP không thể xử lý tên tệp không phải ASCII

Mặc dù cả Windows và Ubuntu đều có thể xử lý tên tệp Unicode (thậm chí là cả RTL), PHP 5.3 yêu cầu hack để xử lý ngay cả với ISO-8859-1 cũ, vì vậy tốt hơn là chỉ giữ ASCII để đảm bảo an toàn.

2. Chiều dài của các vấn đề về tên tệp (Đặc biệt trên Windows)

Trên Ubuntu, độ dài tối đa mà tên tệp có thể có (mở rộng bao gồm) là 255 (không bao gồm đường dẫn):

/var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/

Tuy nhiên, trên Windows 7 (NTFS) chiều dài tối đa mà tên tệp có thể có tùy thuộc vào đường dẫn tuyệt đối của nó:

(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 nói rằng:

NTFS cho phép mỗi thành phần đường dẫn (thư mục hoặc tên tệp) dài 255 ký tự.

Theo hiểu biết tốt nhất của tôi (và thử nghiệm), điều này là sai.

Tổng cộng (đếm dấu gạch chéo) tất cả các ví dụ này có 259 ký tự, nếu bạn tước C:\ đó cung cấp 256 ký tự (không phải 255?!). Các thư mục được tạo bằng Explorer và bạn sẽ thấy rằng nó hạn chế sử dụng tất cả không gian có sẵn cho tên thư mục. Lý do cho điều này là để cho phép tạo các tệp bằng cách sử dụng quy ước đặt tên tệp 8.3 . Điều tương tự xảy ra cho các phân vùng khác.

Tất nhiên, các tệp không cần phải bảo lưu các yêu cầu chiều dài 8.3:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

Bạn không thể tạo thêm bất kỳ thư mục con nào nếu đường dẫn tuyệt đối của thư mục mẹ có hơn 242 ký tự, bởi vì 256 = 242 + 1 + \ + 8 + . + 3. Sử dụng Windows Explorer, bạn không thể tạo thư mục khác nếu thư mục mẹ có nhiều hơn 233 ký tự (tùy thuộc vào ngôn ngữ hệ thống), bởi vì 256 = 233 + 10 + \ + 8 + . + 3; các 10đây là chiều dài của chuỗiNew folder .

Hệ thống tệp Windows đặt ra một vấn đề khó chịu nếu bạn muốn đảm bảo khả năng tương tác giữa các hệ thống tệp.

3. Cảnh giác với các ký tự và từ khóa dành riêng

Ngoài việc xóa các ký tự không phải ASCII, không thể in và điều khiển , bạn cũng cần phải đặt lại (đặt / di chuyển):

"*/:<>?\|

Chỉ cần loại bỏ các ký tự này có thể không phải là ý tưởng tốt nhất vì tên tệp có thể mất một số ý nghĩa của nó. Tôi nghĩ rằng, ít nhất, nhiều lần xuất hiện của các ký tự này nên được thay thế bằng một dấu gạch dưới ( _) hoặc có thể là một cái gì đó đại diện hơn (đây chỉ là một ý tưởng):

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

Ngoài ra còn có các từ khóa đặc biệt nên tránh (như NUL), mặc dù tôi không chắc chắn làm thế nào để vượt qua điều đó. Có lẽ một danh sách đen với một tên dự phòng ngẫu nhiên sẽ là một cách tiếp cận tốt để giải quyết nó.

4. Phân biệt chữ hoa chữ thường

Điều này không cần phải nói, nhưng nếu bạn muốn đảm bảo tính duy nhất của tệp trên các hệ điều hành khác nhau, bạn nên chuyển đổi tên tệp thành trường hợp chuẩn hóa, theo cách đó my_file.txtMy_File.txttrên Linux sẽ không trở nên giống nhaumy_file.txt tệp trên Windows.

5. Hãy chắc chắn rằng nó là duy nhất

Nếu tên tệp đã tồn tại, một số nhận dạng duy nhất sẽ được thêm vào tên tệp cơ sở của nó.

Các định danh duy nhất phổ biến bao gồm dấu thời gian UNIX, bản tóm tắt nội dung tệp hoặc chuỗi ngẫu nhiên.

6. Tập tin ẩn

Chỉ vì nó có thể được đặt tên không có nghĩa là nó nên ...

Các dấu chấm thường được liệt kê trắng trong tên tệp nhưng trong Linux, một tệp ẩn được thể hiện bằng dấu chấm hàng đầu.

7. Những cân nhắc khác

Nếu bạn phải loại bỏ một số ký tự của tên tệp, phần mở rộng thường quan trọng hơn tên cơ sở của tệp. Cho phép số lượng ký tự tối đa đáng kể cho phần mở rộng tệp (8-16), người ta sẽ loại bỏ các ký tự khỏi tên cơ sở. Cũng cần lưu ý rằng trong trường hợp không thể có nhiều phần mở rộng dài - chẳng hạn như _.graphmlz.tag.gz- _.graphmlz.tagchỉ _nên được coi là tên cơ sở của tệp trong trường hợp này.

8. Tài nguyên

Calibre xử lý tên tập tin xéo khá đẹp:

Trang Wikipedia về xáo trộn tên tệpchương được liên kết từ Sử dụng Samba .


Ví dụ: bạn cố gắng tạo một tệp vi phạm bất kỳ quy tắc 1/2/3 nào, bạn sẽ gặp một lỗi rất hữu ích:

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

11

Tôi đã luôn nghĩ Kohana đã làm rất tốt công việc đó .

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

Tiện dụng UTF8::transliterate_to_ascii()sẽ biến những thứ như ñ => n.

Tất nhiên, bạn có thể thay thế các UTF8::*công cụ khác bằng các hàm mb_ ​​*.


5

Về mặt tải lên tệp, bạn sẽ an toàn nhất để ngăn người dùng kiểm soát tên tệp. Như đã được gợi ý, lưu trữ tên tệp được chuẩn hóa trong cơ sở dữ liệu cùng với một tên duy nhất được chọn ngẫu nhiên và duy nhất mà bạn sẽ sử dụng làm tên tệp thực tế.

Sử dụng OWASP ESAPI, những tên này có thể được tạo như vậy:

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

Bạn có thể nối dấu thời gian vào $ safeFilename để giúp đảm bảo rằng tên tệp được tạo ngẫu nhiên là duy nhất mà không cần kiểm tra tệp hiện có.

Về mặt mã hóa cho URL và một lần nữa sử dụng ESAPI:

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

Phương pháp này thực hiện chuẩn hóa trước khi mã hóa chuỗi và sẽ xử lý tất cả các mã hóa ký tự.


Chắc chắn - cũng vậy, việc lấy quyền kiểm soát tên tệp khỏi người dùng sẽ ngăn khả năng 2 lượt tải lên có cùng tên.
CodeVirtuoso

5

Tôi khuyên dùng * URLify cho PHP (hơn 480 sao trên Github) - "cổng PHP của URLify.js từ dự án Django. Chuyển ngữ các ký tự không phải mã ascii để sử dụng trong URL".

Cách sử dụng cơ bản:

Để tạo sên cho 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"

?>

Để tạo sên cho tên tệp:

<?php

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

?>

* Không có đề xuất nào khác phù hợp với tiêu chí của tôi:

  • Nên cài đặt qua trình soạn thảo
  • Không nên phụ thuộc vào iconv vì nó hoạt động khác nhau trên các hệ thống khác nhau
  • Nên được mở rộng để cho phép ghi đè và thay thế ký tự tùy chỉnh
  • Phổ biến (ví dụ nhiều ngôi sao trên Github)
  • Có bài kiểm tra

Là một phần thưởng, URLify cũng loại bỏ một số từ nhất định và loại bỏ tất cả các ký tự không được phiên âm.

Dưới đây là trường hợp thử nghiệm với hàng tấn ký tự nước ngoài được phiên âm đúng cách bằng URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f


1
Cảm ơn - có vẻ lý tưởng cho mục đích của tôi.
David Goodwin

5

Tôi đã điều chỉnh từ một nguồn khác và thêm một vài chi tiết, có thể hơi quá mức

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

và đây là phiên bản Joomla 3.3.2 từ 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

Tôi không nghĩ rằng có một danh sách các ký tự để loại bỏ là an toàn. Tôi muốn sử dụng như sau:

Đối với tên tệp: Sử dụng ID nội bộ hoặc hàm băm của tệp filecontent. Lưu tên tài liệu trong cơ sở dữ liệu. Bằng cách này bạn có thể giữ tên tệp gốc và vẫn tìm thấy tệp.

Đối với tham số url: Sử dụng urlencode()để mã hóa bất kỳ ký tự đặc biệt nào.


1
Tôi đồng ý, hầu hết các phương pháp được liệt kê ở đây đều loại bỏ các ký tự nguy hiểm đã biết - phương pháp của tôi loại bỏ mọi thứ không phải là ký tự an toàn đã biết . Vì hầu hết các hệ thống sên mã hóa URL bài đăng, tôi sẽ đề nghị chúng tôi tiếp tục theo phương pháp đã được chứng minh này thay vì sử dụng urlencode không an toàn UTF-8 ().
Xeoncross

3

Tùy thuộc vào cách bạn sẽ sử dụng nó, bạn có thể muốn thêm giới hạn độ dài để bảo vệ chống tràn bộ đệm.


Có, kiểm tra cho mb_strlen () luôn là một điều quan trọng!
Xeoncross

3

Đây là một cách hay để bảo mật tên tệp tải lên:

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

Tôi không chắc chắn về điều này, vì người ta .\x00..\x20có thể giảm xuống .\x00\x20.
Xeoncross

@Xeoncross: Tôi nghĩ rằng .\x00..\x20loại bỏ các dấu chấm và mọi ký tự giữa \x00\x20, trong khi đó .\x00\x20chỉ nên loại bỏ 3 byte đó.
Alix Axel

Câu trả lời này yêu cầu giải thích nhiều hơn để nó được sử dụng một cách an toàn. Không có nhiều thông tin về cú pháp chính xác cho danh sách từ thiện trên mạng.
Manuel Arwed Schmidt

3

Đây là cách triển khai của 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));
}

Và sự remove_invisible_charactersphụ thuộc.

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

Tại sao không chỉ đơn giản là sử dụng php urlencode? nó thay thế các ký tự "nguy hiểm" bằng biểu diễn hex của chúng cho các url (nghĩa là %20cho một khoảng trắng)


2
% Ký tự không được khuyến nghị cho tên tệp và ký tự được mã hóa hex trông không đẹp trong URL. Các trình duyệt có thể hỗ trợ các chuỗi UTF-8 đẹp hơn và dễ dàng hơn cho các ngôn ngữ không phải là ascii.
Xeoncross

bạn có thể thực hiện một urlencode và THEN một str numplace ('% 20', '-', url)?
Francesco

2

Đã có một số giải pháp được cung cấp cho câu hỏi này nhưng tôi đã đọc và kiểm tra hầu hết các mã ở đây và tôi đã kết thúc với giải pháp này, đó là sự pha trộn của những gì tôi học được ở đây:

Chức năng

Hàm được gói ở đây trong gói Symfony2 nhưng nó có thể được trích xuất để sử dụng như PHP đơn giản , nó chỉ có một phụ thuộc vớiiconv chức năng phải được kích hoạt:

Hệ thống tập tin.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;
    }
}

Các bài kiểm tra đơn vị

Điều thú vị là tôi đã tạo các bài kiểm tra PHPUnit, trước tiên để kiểm tra các trường hợp cạnh và vì vậy bạn có thể kiểm tra xem nó có phù hợp với nhu cầu của bạn không: (Nếu bạn tìm thấy một lỗi, vui lòng thêm trường hợp kiểm tra)

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

Kết quả kiểm tra: (đã kiểm tra trên Ubuntu với PHP 5.3.2 và MacOsX với 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
Điều này giả định chủ yếu là đầu vào dựa trên tiếng Latin. Thêm nhiều ký tự UTF-8 từ các ngôn ngữ khác để xem bạn sẽ gặp vấn đề ở đâu.
Xeoncross

@Xeoncross Tôi đồng ý, như Christian nói người ta phải lưu Id hoặc băm VÀ tên tệp gốc. Nhưng chức năng này cung cấp một sự thay thế vì bạn có thể chỉ định một chuỗi mặc định khi quá trình khử trùng không thành công. Tôi đã thêm một bài kiểm tra đơn vị cho trường hợp này. Cảm ơn đã báo cáo lỗi.
COil

2

Tôi có tiêu đề mục nhập với tất cả các loại ký tự Latin kỳ lạ cũng như một số thẻ HTML mà tôi cần để dịch sang định dạng tên tệp được phân tách bằng dấu gạch ngang hữu ích. Tôi đã kết hợp câu trả lời của @ SoLoGHoST với một vài mục từ câu trả lời của @ Xeoncross và tùy chỉnh một chút.

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

Tôi cần phải tự thêm ký tự em dash (-) vào mảng dịch. Có thể có những người khác nhưng cho đến nay tên tập tin của tôi đang tìm kiếm tốt.

Vì thế:

Phần 1: Bố “urburts của cha tôi? - họ (không phải) giỏi nhất!

trở thành:

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

Tôi chỉ cần thêm ".html" vào chuỗi trả về.


1
Vẫn còn thiếu một số nhân vật Séc và Slovakia:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet

1
Và không còn nghi ngờ gì nữa. Tôi thực sự đang cố gắng tìm hiểu xem có tồn tại một bộ ISO bao gồm các tổ hợp ký tự không. Làm thế nào để "chọn" một bộ nếu nội dung yêu cầu các ký tự từ tất cả chúng? UTF-8 Tôi đang giả sử ...
cbmtrx

Tôi đã tìm ra cách chuyển ngữ bất kỳ chuỗi nào bằng một dòng PHP : $string = transliterator_transliterate('Any-Latin;Latin-ASCII;', $string);Xem câu trả lời của tôi bên dưới hoặc đọc bài đăng trên blog được liên kết.
Jasom Dotnet

1
Không, bạn đã đọc sai: NẾU bạn có thể cài đặt các phần mở rộng PHP trên máy chủ của mình (hoặc lưu trữ) :-) Đây là bài đăng .
Jasom Dotnet

1
À, hiểu rồi Cảm ơn @JasomDotnet - Tôi có giải pháp hiện tại của tôi hiện đang hoạt động nhưng đó là một bộ ký tự giới hạn để tiện ích mở rộng đáng để kiểm tra.
cbmtrx

2

Giải pháp số 1: Bạn có khả năng cài đặt các phần mở rộng PHP trên máy chủ (lưu trữ)

Để phiên âm từ "hầu hết mọi ngôn ngữ trên hành tinh Trái đất" sang các ký tự ASCII.

  1. Cài đặt phần mở rộng PHP Intl trước. Đây là lệnh cho Debian (Ubuntu):sudo aptitude install php5-intl

  2. Đây là hàm fileName của tôi (tạo test.php và dán mã sau đây):

<!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>

Dòng này là cốt lõi:

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

Trả lời dựa trên bài này .

Giải pháp số 2: Bạn không có khả năng cài đặt các tiện ích mở rộng PHP trên máy chủ (lưu trữ)

nhập mô tả hình ảnh ở đây

Công việc khá tốt được thực hiện trong mô đun chuyển ngữ cho CMS Drupal. Nó hỗ trợ hầu hết mọi ngôn ngữ trên hành tinh Trái đất. Tôi đề nghị kiểm tra kho lưu trữ plugin nếu bạn muốn có chuỗi giải pháp vệ sinh thực sự hoàn chỉnh.



1

Đây là một chức năng tốt:

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

Điều này có vẻ xấu. \\s+có nghĩa là dấu gạch chéo ngược theo sau bởi một hoặc nhiều khoảng trắng. Đó là những gì về? Ngoài ra, điều này sử dụng danh sách đen thay vì danh sách trắng bỏ qua những thứ như CMD, null hoặc BEL.
Xeoncross

Vẫn tệ. Bây giờ chuỗi như /blog/2014-02/just-in-timekhông được phép. Vui lòng sử dụng mã được kiểm tra ở trên hoặc sử dụng phunctionmã khung PHP.
Xeoncross

Đúng rồi. Chức năng này chỉ dành cho phần "chỉ trong thời gian". Có thể hữu ích cho một số người.
joan16v

1
Bạn có thể thay đổi regexpreg_replace('~[^\-\pL\pN\s]+~u', '-', $string)
Xeoncross

Tuyệt vời! Tôi cũng đã thêm: string = trim ($ string, "-");
joan16v

0

Đây là mã được sử dụng bởi Prestashop để vệ sinh các url:

replaceAccentedChars

được sử dụng bởi

str2url

để loại bỏ dấu phụ

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

} 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.