น้ำยาฆ่าเชื้อสตริงสำหรับชื่อไฟล์


113

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

(ฉันเขียนได้ แต่ฉันกังวลว่าฉันจะมองข้ามตัวละครไป!)

แก้ไข: สำหรับบันทึกไฟล์ในระบบไฟล์ Windows NTFS


1
คุณสามารถเจาะจงได้มากขึ้น: จะเกิดอะไรขึ้นกับ Umlauts (ลบหรือแปลงเป็นอักขระพื้นฐาน) จะเกิดอะไรขึ้นกับอักขระพิเศษ
Pekka

สำหรับระบบไฟล์ใด พวกเขาแตกต่างกัน ดูen.wikipedia.org/wiki/…
Gordon

Windows :) ต้องการอักขระ 15 ตัว
user151841

1
ฉันต้องการชี้ให้เห็นว่าโซลูชัน "บัญชีดำ" ที่แนะนำในคำตอบบางส่วนนั้นไม่เพียงพอเนื่องจากไม่สามารถตรวจสอบอักขระที่ไม่ต้องการได้ทุกตัว (นอกเหนือจากอักขระพิเศษแล้วยังมีอักขระที่มีเครื่องหมายเน้นเสียงและเครื่องหมายอุทานทั้งตัว ตัวอักษรที่ไม่ใช่ภาษาอังกฤษ / ละตินอักขระควบคุม ฯลฯ ที่ต้องจัดการ) ดังนั้นฉันขอยืนยันว่าวิธีการ "รายการที่อนุญาตพิเศษ" นั้นดีกว่าเสมอและการทำให้สตริงเป็นปกติ (ตามที่แนะนำโดยความคิดเห็นของแบลร์แมคมิลแลนเกี่ยวกับคำตอบของโดมินิกร็อดเจอร์) จะช่วยให้สามารถจัดการกับตัวอักษรใด ๆ ที่มีสำเนียงเครื่องหมายอุมเลาท์ ฯลฯ ได้อย่างเป็นธรรมชาติ
ฌอนเดอะบีน

วิธีที่ดีอาจใช้นิพจน์ทั่วไปดูสคริปต์ python ที่ฉันสร้างขึ้น: github.com/gsscoder/normalize-fn
gsscoder

คำตอบ:


42

แทนที่จะกังวลเกี่ยวกับการมองเห็นตัวละครคุณยินดีที่จะใช้ตัวละครในรายการที่อนุญาตพิเศษหรือไม่? ตัวอย่างเช่นคุณอาจทำให้ OL เพียงดี' a-z, 0-9, _และเช่นเดียวของจุด ( .) เห็นได้ชัดว่ามีข้อ จำกัด มากกว่าระบบไฟล์ส่วนใหญ่ แต่ควรทำให้คุณปลอดภัย


40
ไม่เหมาะสำหรับภาษาที่มี Umlauts สิ่งนี้จะส่งผลให้ Qubec สำหรับQuébec, Dsseldorf สำหรับDüsseldorfและอื่น ๆ
Pekka

15
จริง - แต่อย่างที่ฉันพูด: "ตัวอย่าง"
Dominic Rodger

5
ซึ่งอาจเป็นที่ยอมรับอย่างสมบูรณ์สำหรับ OP. ไม่เช่นนั้นให้ใช้php.net/manual/en/class.normalizer.php
Blair McMillan

3
นั่นไม่ใช่สิ่งที่ถามจริง หน่วยปฏิบัติการขอฟังก์ชันในการฆ่าเชื้อสตริงไม่ใช่ทางเลือกอื่น
i.am.michiel

3
@ i.am.michiel บางที แต่ถ้า OP ยอมรับแล้วฉันจะถือว่าพวกเขาพบว่ามันมีประโยชน์
Dominic Rodger

157

การปรับแก้ปัญหาของ Tor Valamo เล็กน้อยเพื่อแก้ไขปัญหาที่ Dominic Rodger สังเกตเห็นคุณสามารถใช้:

// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_~,;[]().
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
$file = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $file);
// Remove any runs of periods (thanks falstro!)
$file = mb_ereg_replace("([\.]{2,})", '', $file);

43
ฉันรัก regex junkies! -_ ~
AVProgrammer

2
@ iim.hlk - ใช่มันไม่มีวงเล็บปิด ฉันได้เพิ่มสิ่งเหล่านั้นแล้ว ขอบคุณ!
Sean Vieira

2
มีข้อบกพร่องอยู่ในนั้นคุณควรแบ่งออกเป็นสองส่วนและเรียกใช้การตรวจสอบใน..ภายหลัง ยกตัวอย่างเช่นจะจบลงด้วยการ.?. ..แม้ว่าคุณจะกรอง/ฉันไม่เห็นว่าคุณใช้ประโยชน์จากสิ่งนั้นได้อย่างไรในตอนนี้ แต่ก็แสดงให้เห็นว่าทำไมการตรวจสอบ..จึงไม่ได้ผลที่นี่ ยังดีกว่าอย่าแทนที่เพียงแค่ปฏิเสธหากไม่มีคุณสมบัติ
falstro

2
เนื่องจากไม่มีค่าใดที่ผิดกฎหมายในระบบไฟล์ Windowsและเหตุใดจึงต้องใส่ข้อมูลมากกว่าที่คุณต้องการ คุณสามารถเปลี่ยนนิพจน์ทั่วไปเป็นเพียงแค่[^a-z0-9_-]ถ้าคุณต้องการ จำกัด จริงๆหรือเพียงแค่ใช้ชื่อที่สร้างขึ้นแล้วทิ้งชื่อที่กำหนดและหลีกเลี่ยงปัญหาเหล่านี้ทั้งหมด :-)
Sean Vieira

3
โปรดทราบว่า: ผิดกฎหมาย
JasonXA

50

นี่คือวิธีการล้างระบบไฟล์ตามที่ถาม

function filter_filename($name) {
    // remove illegal file system characters https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
    $name = str_replace(array_merge(
        array_map('chr', range(0, 31)),
        array('<', '>', ':', '"', '/', '\\', '|', '?', '*')
    ), '', $name);
    // maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($name, PATHINFO_EXTENSION);
    $name= mb_strcut(pathinfo($name, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($name)) . ($ext ? '.' . $ext : '');
    return $name;
}

ทุกอย่างได้รับอนุญาตในระบบไฟล์ดังนั้นคำถามจึงได้รับคำตอบอย่างสมบูรณ์ ...

... แต่การอนุญาตให้ใช้เครื่องหมายคำพูดเดี่ยวในชื่อไฟล์อาจเป็นอันตรายได้'หากคุณใช้ในภายหลังในบริบท HTML ที่ไม่ปลอดภัยเนื่องจากชื่อไฟล์ตามกฎหมายนี้:

 ' onerror= 'alert(document.cookie).jpg

กลายเป็นรู XSS :

<img src='<? echo $image ?>' />
// output:
<img src=' ' onerror= 'alert(document.cookie)' />

ด้วยเหตุนี้ซอฟต์แวร์ CMS ยอดนิยมWordpress จึงลบออก แต่ครอบคลุมตัวอักษรที่เกี่ยวข้องทั้งหมดหลังจากการอัปเดตบางส่วนเท่านั้น:

$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
// ... a few rows later are whitespaces removed as well ...
preg_replace( '/[\r\n\t -]+/', '-', $filename )

ในที่สุดรายการของพวกเขารวมถึงตอนนี้ส่วนใหญ่ของตัวละครที่เป็นส่วนหนึ่งของURI rerserved อักขระและURL อักขระที่ไม่ปลอดภัยรายการ

แน่นอนว่าคุณสามารถเข้ารหัสอักขระเหล่านี้ทั้งหมดในเอาต์พุต HTML ได้ แต่นักพัฒนาส่วนใหญ่และฉันก็ทำตามสำนวน"ดีกว่าปลอดภัยกว่าขอโทษ"และลบออกล่วงหน้า

ในที่สุดฉันขอแนะนำให้ใช้สิ่งนี้:

function filter_filename($filename, $beautify=true) {
    // sanitize filename
    $filename = preg_replace(
        '~
        [<>:"/\\|?*]|            # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
        [\x00-\x1F]|             # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
        [\x7F\xA0\xAD]|          # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
        [#\[\]@!$&\'()+,;=]|     # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
        [{}^\~`]                 # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
        ~x',
        '-', $filename);
    // avoids ".", ".." or ".hiddenFiles"
    $filename = ltrim($filename, '.-');
    // optional beautification
    if ($beautify) $filename = beautify_filename($filename);
    // maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
    return $filename;
}

สิ่งอื่น ๆ ที่ไม่ก่อให้เกิดปัญหากับระบบไฟล์ควรเป็นส่วนหนึ่งของฟังก์ชันเพิ่มเติม:

function beautify_filename($filename) {
    // reduce consecutive characters
    $filename = preg_replace(array(
        // "file   name.zip" becomes "file-name.zip"
        '/ +/',
        // "file___name.zip" becomes "file-name.zip"
        '/_+/',
        // "file---name.zip" becomes "file-name.zip"
        '/-+/'
    ), '-', $filename);
    $filename = preg_replace(array(
        // "file--.--.-.--name.zip" becomes "file.name.zip"
        '/-*\.-*/',
        // "file...name..zip" becomes "file.name.zip"
        '/\.{2,}/'
    ), '.', $filename);
    // lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
    $filename = mb_strtolower($filename, mb_detect_encoding($filename));
    // ".file-name.-" becomes "file-name"
    $filename = trim($filename, '.-');
    return $filename;
}

และ ณ จุดนี้คุณต้องสร้างชื่อไฟล์หากผลลัพธ์ว่างเปล่าและคุณสามารถตัดสินใจได้ว่าคุณต้องการเข้ารหัสอักขระ UTF-8 หรือไม่ แต่คุณไม่จำเป็นต้องใช้เช่นนั้นเนื่องจาก UTF-8 ได้รับอนุญาตในระบบไฟล์ทั้งหมดที่ใช้ในบริบทเว็บโฮสติ้ง

สิ่งเดียวที่คุณต้องทำคือใช้urlencode()(ตามที่คุณหวังไว้กับ URL ทั้งหมดของคุณ) ดังนั้นชื่อไฟล์საბეჭდი_მანქანა.jpgจึงกลายเป็น URL นี้เป็นของคุณ<img src>หรือ<a href>: http://www.maxrev.de/html/img/%E1%83% A1% E1% 83% 90% E1% 83% 91% E1% 83% 94% E1% 83% AD% E1% 83% 93% E1% 83% 98_% E1% 83% 9B% E1% 83% 90% E1% 83% 9C% E1% 83% A5% E1% 83% 90% E1% 83% 9C% E1% 83% 90.jpg

Stackoverflow ทำเช่นนั้นดังนั้นฉันจึงสามารถโพสต์ลิงก์นี้ได้ตามที่ผู้ใช้ทำ:
http://www.maxrev.de/html/img/ საბეჭდი_მანქანა. jpg

ดังนั้นนี่คือชื่อไฟล์ที่สมบูรณ์และถูกต้องตามกฎหมายไม่ได้เป็นปัญหาเป็น@ SequenceDigitale.com กล่าวถึงในคำตอบของเขา


3
ทำได้ดีมาก คำตอบที่เป็นประโยชน์ที่สุดสำหรับฉัน +1

โอ้ ... ฟังก์ชั่นใช้งานได้ดี แต่เมื่อเวลาผ่านไปมันเริ่มวางระหว่างตัวละครทุกตัวเช่นr-u-l-e-sและฉันไม่รู้ว่าทำไมถึงเกิดขึ้น แน่นอนว่ามันไม่ใช่ความผิดของฟังก์ชั่น แต่เพียงแค่ถาม - อะไรคือสาเหตุของพฤติกรรมดังกล่าว? การเข้ารหัสผิด?

1
โอ้ดี ... เพียงแค่ทำให้การแก้ปัญหาและมันเกิดขึ้นหลังในpreg_replace filter_filename()

หลังจากลบความคิดเห็นเหล่านี้แล้วความคิดเห็นก็เริ่มทำงานอีกครั้ง

คุณนำความคิดเห็นใดออก ส่งอีเมลถึงฉันหากวิธีนี้ง่ายกว่า: gutt.it/contact.htm
mgutt

43

แล้วการใช้ rawurlencode () ล่ะ? http://www.php.net/manual/en/function.rawurlencode.php

นี่คือฟังก์ชั่นที่ฆ่าเชื้อแม้แต่อักษรจีน:

public static function normalizeString ($str = '')
{
    $str = strip_tags($str); 
    $str = preg_replace('/[\r\n\t ]+/', ' ', $str);
    $str = preg_replace('/[\"\*\/\:\<\>\?\'\|]+/', ' ', $str);
    $str = strtolower($str);
    $str = html_entity_decode( $str, ENT_QUOTES, "utf-8" );
    $str = htmlentities($str, ENT_QUOTES, "utf-8");
    $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
    $str = str_replace(' ', '-', $str);
    $str = rawurlencode($str);
    $str = str_replace('%', '-', $str);
    return $str;
}

นี่คือคำอธิบาย

  1. ตัดแท็ก HTML
  2. ลบ Break / Tabs / Return Carriage
  3. ลบ Illegal Chars สำหรับโฟลเดอร์และชื่อไฟล์
  4. ใส่สตริงเป็นตัวพิมพ์เล็ก
  5. ลบสำเนียงแปลกปลอมเช่นÉàûโดยแปลงเป็นเอนทิตี html แล้วลบรหัสและเก็บตัวอักษรไว้
  6. แทนที่ช่องว่างด้วยขีดกลาง
  7. เข้ารหัสอักขระพิเศษที่สามารถผ่านขั้นตอนก่อนหน้านี้และป้อนชื่อไฟล์ที่ขัดแย้งบนเซิร์ฟเวอร์ เช่น "中文百强网"
  8. แทนที่ "%" ด้วยเครื่องหมายขีดกลางเพื่อให้แน่ใจว่าลิงก์ของไฟล์จะไม่ถูกเขียนใหม่โดยเบราว์เซอร์เมื่อค้นหาไฟล์ th

ตกลงชื่อไฟล์บางไฟล์จะไม่เกี่ยวข้องกัน แต่โดยส่วนใหญ่แล้วจะใช้งานได้

เช่น ชื่อเดิม: "საბეჭდი-და-ტიპოგრაფიული. jpg"

ชื่อเอาต์พุต: "-E1-83-A1-E1-83-90-E1-83-91-E1-83-94-E1-83-AD-E1-83-93-E1-83-98 - E1- 83-93-E1-83-90 - E1-83-A2-E1-83-98-E1-83-9E-E1-83-9D-E1-83-92-E1-83-A0-E1-83 -90-E1-83-A4-E1-83-98-E1-83-A3-E1-83-9A-E1-83-98.jpg”

มันดีกว่าข้อผิดพลาด 404

หวังว่าจะเป็นประโยชน์

คาร์ล.


1
คุณไม่ได้ลบ NULL และอักขระควบคุม ควรลบ ASCII ที่ 0 ถึง 32 ทั้งหมดออกจากสตริง
Basil Musa

อนุญาตให้ใช้ UTF-8 ในระบบไฟล์และได้รับอนุญาตใน URL ดังนั้นเหตุใดจึงควรสร้างข้อผิดพลาด 404 สิ่งเดียวที่คุณต้องทำคือการเข้ารหัสของ URL http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpgไปhttp://www.maxrev.de/html/img/%E1%83%A1%E1%83%90%E1%83%91%E1%83%94%E1%83%AD%E1%83%93%E1%83%98_%E1%83%9B%E1%83%90%E1%83%9C%E1%83%A5%E1%83%90%E1%83%9C%E1%83%90.jpgในซอร์สโค้ด HTML ที่คุณหวังว่าจะทำอย่างไรกับ URL ของคุณทั้งหมด
mgutt

1
บางจุดอื่น ๆ : คุณลบแท็ก HTML ผ่านและหลังจากที่คุณลบstrip_tags() [<>]โดยที่strip_tags()ไม่จำเป็นเลยจริงๆ จุดเดียวกันคือเครื่องหมายคำพูด ENT_QUOTESมีคำพูดที่ไม่เหลือเมื่อคุณถอดรหัสกับมี และstr_replace()ไม่ลบช่องว่างสีขาวที่ติดต่อกันจากนั้นคุณใช้strtolower()สำหรับสตริงแบบหลายไบต์ แล้วทำไมคุณถึงแปลงเป็นตัวพิมพ์เล็กล่ะ? และในที่สุดคุณก็ไม่พบตัวละครที่สงวนไว้ตามที่ @BasilMusa กล่าวถึง รายละเอียดเพิ่มเติมในคำตอบของฉัน: stackoverflow.com/a/42058764/318765
mgutt

ตกหลุมรักมัน!
Yash Kumar Verma

39

โซลูชันที่ 1 - ง่ายและมีประสิทธิภาพ

$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );

  • strtolower () รับประกันว่าชื่อไฟล์เป็นตัวพิมพ์เล็ก (เนื่องจากตัวพิมพ์ไม่สำคัญภายใน URL แต่อยู่ในชื่อไฟล์ NTFS)
  • [^a-z0-9]+ เพื่อให้แน่ใจว่าชื่อไฟล์จะเก็บเฉพาะตัวอักษรและตัวเลขเท่านั้น
  • แทนที่อักขระที่ไม่ถูกต้องโดย'-'ให้ชื่อไฟล์อ่านได้

ตัวอย่าง:

URL:  http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename
File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename

โซลูชัน 2 - สำหรับ URL ที่ยาวมาก

คุณต้องการแคชเนื้อหา URL และต้องมีชื่อไฟล์เฉพาะ ฉันจะใช้ฟังก์ชันนี้:

$file_name = md5( strtolower( $url ) )

สิ่งนี้จะสร้างชื่อไฟล์ที่มีความยาวคงที่ ในกรณีส่วนใหญ่แฮช MD5 มีเอกลักษณ์เพียงพอสำหรับการใช้งานประเภทนี้

ตัวอย่าง:

URL:  https://www.amazon.com/Interstellar-Matthew-McConaughey/dp/B00TU9UFTS/ref=s9_nwrsa_gw_g318_i10_r?_encoding=UTF8&fpl=fresh&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=desktop-1&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_t=36701&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_i=desktop
File: 51301f3edb513f6543779c3a5433b01c

4
บางที MD5 อาจมีปัญหา: โปรดใช้ความระมัดระวังเมื่อใช้แฮชกับ URL ในขณะที่รากที่สองของหมายเลขskrenta.com/2007/08/md5_tutorial.htmlของ URL ยังคงใหญ่กว่ามากดังนั้นขนาดเว็บปัจจุบันหากคุณเกิดการชนกันคุณจะได้รับหน้าเว็บเกี่ยวกับ Britney Spears เมื่อคุณคาดหวังว่าจะมีหน้า เกี่ยวกับ Bugzilla อาจไม่ใช่ปัญหาในกรณีของเรา แต่สำหรับหลายพันล้านเพจฉันจะเลือกใช้อัลกอริทึมการแฮชที่ใหญ่กว่ามากเช่น SHA 256 หรือหลีกเลี่ยงโดยสิ้นเชิง ที่มา: boyter.org/2013/01/code-for-a-search-engine-in-php-part-1
adilbo

15

tempnam () จะทำเพื่อคุณ

http://us2.php.net/manual/en/function.tempnam.php

แต่นั่นเป็นการสร้างชื่อใหม่ทั้งหมด

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

$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);

13
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)

เพิ่ม / ลบอักขระที่ถูกต้องเพิ่มเติมขึ้นอยู่กับสิ่งที่อนุญาตสำหรับระบบของคุณ

หรือคุณสามารถลองสร้างไฟล์แล้วส่งคืนข้อผิดพลาดหากไฟล์ไม่ดี


5
ซึ่งจะอนุญาตผ่านชื่อไฟล์เช่น..ซึ่งอาจเป็นปัญหาหรือไม่ก็ได้
Dominic Rodger

@Dom - เพียงตรวจสอบแยกต่างหากเนื่องจากเป็นค่าคงที่
Tor Valamo

10

PHP มีฟังก์ชันในการล้างข้อความให้เป็นรูปแบบอื่น

filter.filters.sanitize

ทำอย่างไร :

echo filter_var(
   "Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL
); 

Blockquote LoremIpsumhasbeentheindustry's


1
ดี แต่จะไม่ลบเครื่องหมายทับซึ่งอาจเป็นปัญหาได้: Directory traversing
func0der

7

ปลอดภัย: แทนที่ทุกลำดับของ NOT "a-zA-Z0-9_-" เป็นเส้นประ เพิ่มส่วนขยายด้วยตัวคุณเอง

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).'.'.$extension;

1
คุณต้องเพิ่มนามสกุลไฟล์ที่คั่นด้วย ".": $ name = preg_replace ('/ [^ a-zA-Z0-9 _-] + /', '-', strtolower ($ name)) '.' . $ นามสกุล;
Smith

6

นิพจน์ต่อไปนี้สร้างสตริงที่ดีสะอาดและใช้งานได้:

/[^a-z0-9\._-]+/gi

เปลี่ยนการเงินของวันนี้: การเรียกเก็บเงินเป็นการเรียกเก็บเงินในวันนี้


ชื่อไฟล์จึงต้องไม่มีจุดหรือขีดล่างหรืออะไรทำนองนั้น?
Tor Valamo

2
@ โจนาธาน - ตัวเอียงคืออะไร?
Dominic Rodger

@ จขกท. ขอโทษค่ะ อัปเดตแล้ว @ โดมินิกแค่เน้นข้อความ
Sampson

gism คืออะไร? ฉันได้รับ "คำเตือน: preg_replace () [function.preg-replace]: Unknown modifier 'g'"
user151841

1
@ user151841 สำหรับpreg_replaceค่าสถานะส่วนกลางเป็นนัย ดังนั้นจึงไม่จำเป็นต้องใช้ g หากมีการใช้ preg_replace เมื่อเราต้องการควบคุมจำนวนการแทนที่ preg_replace มีlimitพารามิเตอร์สำหรับสิ่งนั้น อ่านเอกสาร preg_replace สำหรับข้อมูลเพิ่มเติม
rineez


2

สิ่งเหล่านี้อาจจะหนักไปหน่อย แต่ก็มีความยืดหยุ่นเพียงพอที่จะล้างสตริงใด ๆ ให้เป็นenชื่อไฟล์หรือชื่อโฟลเดอร์สไตล์"ปลอดภัย" (หรือห่าแม้กระทั่งทากขัดและสิ่งต่างๆหากคุณงอ)

1) การสร้างชื่อไฟล์แบบเต็ม (ด้วยชื่อทางเลือกในกรณีที่อินพุตถูกตัดทอนทั้งหมด):

str_file($raw_string, $word_separator, $file_extension, $fallback_name, $length);

2) หรือใช้เพียงตัวกรอง util โดยไม่ต้องสร้างชื่อไฟล์แบบเต็ม (โหมดเข้มงวดtrueจะไม่อนุญาตให้ใช้ [] หรือ () ในชื่อไฟล์):

str_file_filter($string, $separator, $strict, $length);

3) และนี่คือฟังก์ชั่นเหล่านี้:

// Returns filesystem-safe string after cleaning, filtering, and trimming input
function str_file_filter(
    $str,
    $sep = '_',
    $strict = false,
    $trim = 248) {

    $str = strip_tags(htmlspecialchars_decode(strtolower($str))); // lowercase -> decode -> strip tags
    $str = str_replace("%20", ' ', $str); // convert rogue %20s into spaces
    $str = preg_replace("/%[a-z0-9]{1,2}/i", '', $str); // remove hexy things
    $str = str_replace("&nbsp;", ' ', $str); // convert all nbsp into space
    $str = preg_replace("/&#?[a-z0-9]{2,8};/i", '', $str); // remove the other non-tag things
    $str = preg_replace("/\s+/", $sep, $str); // filter multiple spaces
    $str = preg_replace("/\.+/", '.', $str); // filter multiple periods
    $str = preg_replace("/^\.+/", '', $str); // trim leading period

    if ($strict) {
        $str = preg_replace("/([^\w\d\\" . $sep . ".])/", '', $str); // only allow words and digits
    } else {
        $str = preg_replace("/([^\w\d\\" . $sep . "\[\]\(\).])/", '', $str); // allow words, digits, [], and ()
    }

    $str = preg_replace("/\\" . $sep . "+/", $sep, $str); // filter multiple separators
    $str = substr($str, 0, $trim); // trim filename to desired length, note 255 char limit on windows

    return $str;
}


// Returns full file name including fallback and extension
function str_file(
    $str,
    $sep = '_',
    $ext = '',
    $default = '',
    $trim = 248) {

    // Run $str and/or $ext through filters to clean up strings
    $str = str_file_filter($str, $sep);
    $ext = '.' . str_file_filter($ext, '', true);

    // Default file name in case all chars are trimmed from $str, then ensure there is an id at tail
    if (empty($str) && empty($default)) {
        $str = 'no_name__' . date('Y-m-d_H-m_A') . '__' . uniqid();
    } elseif (empty($str)) {
        $str = $default;
    }

    // Return completed string
    if (!empty($ext)) {
        return $str . $ext;
    } else {
        return $str;
    }
}

สมมติว่าอินพุตของผู้ใช้บางส่วนคือ: .....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული

และเราต้องการแปลงเป็นสิ่งที่เป็นมิตรกว่าเพื่อสร้าง tar.gz ด้วยชื่อไฟล์ที่มีความยาว 255 ตัวอักษร นี่คือตัวอย่างการใช้งาน หมายเหตุ: ตัวอย่างนี้มีส่วนขยาย tar.gz ที่มีรูปแบบไม่ถูกต้องเพื่อเป็นหลักฐานยืนยันแนวคิดคุณยังควรกรองส่วนต่อขยายหลังจากสร้างสตริงขึ้นจากรายการที่อนุญาตของคุณ

$raw_str = '.....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name  %20   %20 %21 %2C Décor  \/.  /. .  z \... y \...... x ./  “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული';
$fallback_str = 'generated_' . date('Y-m-d_H-m_A');
$bad_extension = '....t&+++a()r.gz[]';

echo str_file($raw_str, '_', $bad_extension, $fallback_str);

ผลลัพธ์จะเป็น: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

คุณสามารถเล่นได้ที่นี่: https://3v4l.org/iSgi8

หรือ Gist: https://gist.github.com/dhaupin/b109d3a8464239b7754a

แก้ไข: ตัวกรองสคริปต์ที่อัปเดต&nbsp;แทนช่องว่างลิงก์ 3v4l ที่อัปเดต


1

สิ่งที่ดีที่สุดที่ฉันรู้ในวันนี้คือStringsวิธีการแบบคงที่:: webalizeจาก Nette framework

BTW สิ่งนี้แปลสัญญาณกำกับเสียงทั้งหมดเป็นพื้นฐาน .. š => s ü => u ß => ss เป็นต้น

สำหรับชื่อไฟล์คุณต้องเพิ่มจุด "." ถึงพารามิเตอร์อักขระที่อนุญาต

/**
 * Converts to ASCII.
 * @param  string  UTF-8 encoding
 * @return string  ASCII
 */
public static function toAscii($s)
{
    static $transliterator = NULL;
    if ($transliterator === NULL && class_exists('Transliterator', FALSE)) {
        $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
    }

    $s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
    $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
    $s = str_replace(
        array("\xE2\x80\x9E", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x9A", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xB0"),
        array("\x03", "\x03", "\x03", "\x02", "\x02", "\x02", "\x04"), $s
    );
    if ($transliterator !== NULL) {
        $s = $transliterator->transliterate($s);
    }
    if (ICONV_IMPL === 'glibc') {
        $s = str_replace(
            array("\xC2\xBB", "\xC2\xAB", "\xE2\x80\xA6", "\xE2\x84\xA2", "\xC2\xA9", "\xC2\xAE"),
            array('>>', '<<', '...', 'TM', '(c)', '(R)'), $s
        );
        $s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); // intentionally @
        $s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
            . "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
            . "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
            . "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
            . "\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
            'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.');
        $s = preg_replace('#[^\x00-\x7F]++#', '', $s);
    } else {
        $s = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); // intentionally @
    }
    $s = str_replace(array('`', "'", '"', '^', '~', '?'), '', $s);
    return strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
}


/**
 * Converts to web safe characters [a-z0-9-] text.
 * @param  string  UTF-8 encoding
 * @param  string  allowed characters
 * @param  bool
 * @return string
 */
public static function webalize($s, $charlist = NULL, $lower = TRUE)
{
    $s = self::toAscii($s);
    if ($lower) {
        $s = strtolower($s);
    }
    $s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
    $s = trim($s, '-');
    return $s;
}

ทำไมคุณถึงต้องการแทนที่ตัวกำกับเสียง? เพียงใช้urlencode()ก่อนที่คุณจะใช้ชื่อไฟล์เป็นsrcหรือhref. ระบบไฟล์เดียวที่ใช้ในปัจจุบันที่มีปัญหากับ UTF-8 คือ FATx (ใช้โดย XBOX): en.wikipedia.org/wiki/Comparison_of_file_systems#Limitsและฉันไม่คิดว่าเว็บเซิร์ฟเวอร์นี้ใช้

1

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

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

หากเป็นไปได้ที่จะทำสิ่งชั่วร้ายด้วยชื่อไฟล์อาจมีมาตรการที่สามารถนำไปใช้ก่อนที่จะทดสอบชื่อไฟล์บนระบบปฏิบัติการประจำถิ่นซึ่งจะวัดความซับซ้อนน้อยกว่า "การสุขาภิบาล" ของชื่อไฟล์


0

ทางเดียว

$bad='/[\/:*?"<>|]/';
$string = 'fi?le*';

function sanitize($str,$pat)
{
    return preg_replace($pat,"",$str);

}
echo sanitize($string,$bad);

แล้วตัวอักษรที่พิมพ์ไม่ได้ล่ะ? การใช้แนวทางรายการสีขาวจะดีกว่าวิธีการบัญชีดำในกรณีนี้ โดยทั่วไปอนุญาตเฉพาะชื่อไฟล์ ASCII ที่สามารถพิมพ์ได้ไม่รวมตัวอักษรพิเศษแน่นอน แต่สำหรับภาษาที่ไม่ใช่ภาษาอังกฤษนั่นเป็นอีกปัญหาหนึ่ง
TheRealChx101

0

/และ..ในชื่อไฟล์ที่ผู้ใช้ระบุอาจเป็นอันตรายได้ ดังนั้นคุณควรกำจัดสิ่งเหล่านี้ด้วยสิ่งต่างๆเช่น:

$fname = str_replace('..', '', $fname);
$fname = str_replace('/',  '', $fname);

ไม่เพียงพอ! ตัวอย่างเช่นชื่อไฟล์ "./.name" จะยังคงแยกออกจากไดเร็กทอรีปัจจุบัน (การลบ .. ไม่ได้ทำอะไรที่นี่ แต่การลบ / จะเปลี่ยน. / เป็น .. และด้วยเหตุนี้จึงแยกออกจากไดเรกทอรีเป้าหมาย)
cemper93

3
@ cemper93 ไม่คำตอบนี้จะทำให้สตริงกลายเป็น..nameที่จะไม่แตกออกจากอะไร การลบอักขระตัวคั่นพา ธ ทั้งหมดควรเพียงพอที่จะป้องกันการข้ามผ่านไดเร็กทอรี (การลบออก..เป็นสิ่งที่ไม่จำเป็นในทางเทคนิค)
cdhowie

@cdhowie ใช่ แต่ชื่อไฟล์./.จะกลายเป็น... และในที่สุดคำตอบนี้ก็คิดถึงอักขระสงวนของระบบไฟล์อื่น ๆ เช่น NULL เพิ่มเติมในคำตอบของฉัน: stackoverflow.com/a/42058764/318765
mgutt

-4

$ fname = str_replace ('/', '', $ fname);

เนื่องจากผู้ใช้อาจใช้เครื่องหมายทับเพื่อแยกสองคำจึงควรแทนที่ด้วยเส้นประแทน NULL


ที่ไหนบอกว่าเขาจะแทนที่ด้วย NULL? นอกจากนี้ยังไม่รองรับอักขระพิเศษทั้งหมด
Travis Pessetto

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