เป็นอันตรายหรือไม่หากใช้เสียงสะท้อน


11

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

ฉันเห็นรหัสนี้และสงสัยว่าถ้าเป็นไปได้ที่จะฉีดบางสิ่งให้ทำงานเมื่อบรรทัดของรหัสนี้ทำงาน:

echo run after_bundle


ฉันวิ่งเข้าไปหาสิ่งนี้เมื่อฉันมี: target = "*** เซิร์ฟเวอร์สด ***"; echo target: $ target; และ *** ขยายไปสู่รายชื่อโฟลเดอร์ ...
Matt

คำตอบ:


17

สำหรับกรณีเฉพาะ

echo run after_bundle

ไม่จำเป็นต้องมีการอ้างอิง ไม่จำเป็นต้องมีการอ้างถึงเนื่องจากอาร์กิวเมนต์echoเป็นสตริงแบบสแตติกที่ไม่มีการขยายตัวแปรหรือการแทนที่คำสั่ง ฯลฯ พวกมันคือ "แค่สองคำ" (และเมื่อStéphaneชี้ให้เห็นพวกมันถูกสร้างขึ้นจากชุดอักขระแบบพกพา )

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

คำถามสองข้อต่อไปนี้มีข้อมูลที่เกี่ยวข้องเกี่ยวกับเรื่องนั้น:


echoบางครั้งใช้เพื่อ "ป้องกัน" คำสั่งที่อาจเป็นอันตรายในคำตอบในเว็บไซต์นี้ ตัวอย่างเช่นฉันอาจแสดงวิธีลบไฟล์หรือย้ายไฟล์ไปยังปลายทางใหม่โดยใช้

echo rm "${name##*/}.txt"

หรือ

echo mv "$name" "/new_dir/$newname"

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

คำสั่งของคุณecho run after_bundleอาจเป็นคำสั่งให้กับผู้ใช้หรืออาจเป็นรหัส "คอมเม้นท์" ที่อันตรายเกินกว่าที่จะรันโดยไม่ทราบผลที่ตามมา

การใช้echoสิ่งนี้จะต้องรู้ว่าคำสั่งที่แก้ไขแล้วและต้องรับประกันว่าคำสั่งที่แก้ไขแล้วนั้นปลอดภัยจริง ๆ(มันอาจจะไม่ได้ถ้ามันมีการเปลี่ยนเส้นทางและการใช้มันบนไพพ์ไลน์นั้นไม่ทำงาน ฯลฯ )


การเพิ่มเครื่องหมายคำพูดนั้นไม่เพียงพอที่จะรู้ว่าเชลล์จะทำอย่างไร - เช่นเดียวกับที่คุณไม่สามารถบอกได้ว่าecho rm "first file.txt" "second file.txt"มันแตกต่างจากecho rm "first" "file.txt" "second" "file.txt"อะไร หากคุณต้องการสร้างคำสั่งเชลล์เป็นเอาต์พุตคุณต้องใช้printf '%q ' rm "first file.txt" "second file.txt"; echoหรือสิ่งที่เทียบเท่าที่สร้างการอ้างอิงไวยากรณ์ใหม่ที่ประเมินถึงการargvส่งผ่าน
Charles Duffy

@CharlesDuffy ฉันจริงๆหวังว่าไม่มีใคร copy-paste แก้จุดบกพร่องออกและวิ่งมันในเปลือก!
Kusalananda

1
การสร้างคำสั่งเชลล์และจากนั้นไพพ์ไปยังshไม่ใช่รูปแบบที่ผิดปกติและเห็นผู้คนถามว่า "ทำไมจึงfooทำงานเมื่อฉันเรียกใช้บนบรรทัดคำสั่ง แต่สคริปต์นี้ที่ปล่อยสตริงที่แน่นอนที่echoอยู่ด้านหน้าของบรรทัดไม่ได้หรือไม่ " เกิดขึ้นตลอดเวลาที่นี่ ยิ่งไปกว่านั้นจุดบกพร่องในการส่งออกจะไม่เป็นประโยชน์หากซ่อนข้อบกพร่องของคุณ - และหากข้อบกพร่องของคุณเกี่ยวข้องกับการอ้างอิงแล้วechoจะไม่เปิดเผยพวกเขา
Charles Duffy

27

เพียงแค่บันทึกพิเศษที่ด้านบนของ@ คำตอบที่ดี

echo run after_bundle

ใช้ได้เพราะไม่มีอักขระใด ๆ ในอาร์กิวเมนต์ 3 ตัวที่ส่งผ่านเพื่อให้echoมีอักขระที่พิเศษสำหรับเชลล์

และ (จุดพิเศษที่ฉันต้องการทำที่นี่) ไม่มีระบบภาษาที่ไบต์เหล่านั้นสามารถแปลเป็นอักขระที่พิเศษสำหรับเชลล์

ตัวละครทุกคนที่อยู่ในสิ่งที่POSIX เรียกชุดอักขระแบบพกพา อักขระเหล่านั้นควรมีอยู่และเข้ารหัสเหมือนกันในชุดอักขระทั้งหมดในระบบ POSIX ²

ดังนั้นบรรทัดคำสั่งนั้นจะถูกตีความเหมือนกันโดยไม่คำนึงถึงโลแคล

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

เช่นใน UTF-8:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

ที่àถูกเข้ารหัสเป็น 0xc3 0xa0 ตอนนี้ถ้าคุณมีบรรทัดของรหัสนั้นในเชลล์สคริปต์และเชลล์สคริปต์นั้นถูกเรียกใช้โดยผู้ใช้ที่ใช้โลแคลที่มีชุดอักขระไม่ใช่ UTF-8 ไบต์สองตัวนั้นอาจทำให้อักขระต่างกันมาก

ตัวอย่างเช่นในfr_FR.ISO8859-15สถานที่เกิดเหตุเป็นภาษาฝรั่งเศสทั่วไปโดยใช้ชุดอักขระไบต์เดียวมาตรฐานที่ครอบคลุมภาษาฝรั่งเศส (เช่นเดียวกับที่ใช้สำหรับภาษายุโรปตะวันตกส่วนใหญ่รวมถึงภาษาอังกฤษ) ที่ 0xc3 ไบต์ถูกตีความว่าเป็นÃตัวอักษรและ 0xa0 เป็นไม่ใช่ ตัวละครทำลายพื้นที่

และในบางระบบเช่นNetBSD³นั้นพื้นที่ที่ไม่ทำลายนั้นถือเป็นอักขระว่าง (ซึ่งisblank()จะคืนค่าจริงมันถูกจับคู่ด้วย[[:blank:]]) และเชลล์เช่นbashนั้นจึงถือว่าเป็นตัวคั่นโทเค็นในไวยากรณ์

ซึ่งหมายความว่าแทนที่จะเรียกใช้echoด้วย$'voil\xc3\xa0'อาร์กิวเมนต์พวกเขาเรียกใช้ด้วย$'voil\xc3'อาร์กิวเมนต์ซึ่งหมายความว่าจะไม่พิมพ์voilàอย่างถูกต้อง

จะได้รับมากยิ่งกับชุดตัวอักษรภาษาจีนเช่น BIG5, BIG5-HKSCS, GB18030, GBK ซึ่งมีตัวละครหลายคนที่มีการเข้ารหัสที่มีการเข้ารหัสเช่นเดียวกับ|, `, \(เพื่อชื่อที่เลวร้ายที่สุด) (ยังเป็นที่น่าหัวเราะ SJIS อาคาไมโครซอฟท์คันจิยกเว้น ว่ามันเป็น¥แทน\แต่ก็ยังถือว่าเป็น\เครื่องมือมากที่สุดเท่าที่มันเข้ารหัสเป็น 0x5c มี)

ตัวอย่างเช่นหากในzh_CN.gb18030ภาษาจีนคุณเขียนสคริปต์เช่น:

echo  reboot

สคริปต์นั้นจะแสดงผล詜 rebootในโลแคลโดยใช้ GB18030 หรือ GBK 唰 rebootในโลแคลที่ใช้ BIG5 หรือ BIG5-HKSCS แต่ในโลแคล C ที่ใช้ ASCII หรือโลแคลที่ใช้ ISO8859-15 หรือ UTF-8 จะทำให้เกิดrebootการเรียกใช้เนื่องจากการเข้ารหัส GB18030 จากis 0xd4 0x7c และ 0x7c เป็นการเข้ารหัสของ|ใน ASCII ดังนั้นเราจึงทำงาน:

 echo �| reboot

( นั้นแทน 0xd4 ไบต์ถูกเรนเดอร์ในโลแคล) ตัวอย่างการใช้อันตรายน้อยกว่าunameแทนreboot:

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

( unameถูกเรียกใช้)

ดังนั้นคำแนะนำของฉันคือการอ้างอิงสตริงทั้งหมดที่มีอักขระนอกชุดอักขระแบบพกพา

อย่างไรก็ตามโปรดทราบว่าเนื่องจากการเข้ารหัส\และ`พบในการเข้ารหัสของอักขระเหล่านั้นบางอย่างจะดีกว่าที่จะไม่ใช้\หรือ"..."หรือ$'...'(ภายในซึ่ง`และ / หรือ\ยังคงเป็นพิเศษ) แต่'...'แทนที่จะอ้างถึงตัวละครนอกชุดอักขระแบบพกพา

ฉันไม่ทราบว่าระบบใดที่มีโลแคลที่ชุดอักขระมีอักขระใด ๆ (นอกเหนือจาก'ตัวของมันเอง) ซึ่งการเข้ารหัสประกอบด้วยการเข้ารหัส'ดังนั้นผู้ที่'...'ควรจะปลอดภัยที่สุด

โปรดทราบว่าเชลล์จำนวนมากยังรองรับ$'\uXXXX'สัญลักษณ์เพื่อแสดงอักขระตามจุดโค้ด Unicode ในเชลล์เช่นzshและbashอักขระจะถูกเข้ารหัสในชุดอักขระของโลแคล (แม้ว่าอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดหากชุดอักขระนั้นไม่มีอักขระนั้น) ที่ช่วยให้คุณหลีกเลี่ยงการใส่อักขระที่ไม่ใช่ ASCII ในรหัสเชลล์ของคุณ

ดังนั้นข้างต้น:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

หรือ:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(ด้วยข้อแม้มันสามารถทำลายสคริปต์เมื่อทำงานในสถานที่ที่ไม่มีตัวอักษรเหล่านั้น)

หรือดีกว่าเนื่องจาก\เป็นพิเศษสำหรับecho(หรืออย่างน้อยการปรับใช้บาง echoอย่างอย่างน้อยที่สอดคล้องกับ Unix):

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

(โปรดทราบว่า\นี่เป็นพิเศษในอาร์กิวเมนต์แรกprintfดังนั้นอักขระที่ไม่ใช่ ASCII จึงหลีกเลี่ยงได้ดีกว่าในกรณีที่อาจมีการเข้ารหัส\)

โปรดทราบว่าคุณสามารถทำได้เช่นกัน:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(นั่นจะเกินความจริง แต่จะให้ความอุ่นใจถ้าคุณไม่แน่ใจว่าตัวละครตัวไหนอยู่ในชุดตัวละครแบบพกพา)

ตรวจสอบให้แน่ใจว่าไม่เคยใช้การ`...`ทดแทนคำสั่งในรูปแบบโบราณ(ซึ่งแนะนำการประมวลผลแบ็กสแลชอีกระดับหนึ่ง) แต่ให้ใช้$(...)แทน


¹เทคนิคechoถูกส่งผ่านไปยังเป็นอาร์กิวเมนต์กับechoยูทิลิตี้ (เพื่อบอกว่าวิธีการที่จะถูกเรียก) มันเป็นargv[0]และargcเป็น 3 แม้ว่าจะอยู่ในเปลือกหอยส่วนใหญ่ในปัจจุบันechoจะ builtin เพื่อให้exec()ของ/bin/echoไฟล์ที่มีรายชื่อของ 3 ข้อโต้แย้งจะถูกจำลองโดย เปลือก. นอกจากนี้ยังเป็นเรื่องธรรมดาที่จะพิจารณารายการอาร์กิวเมนต์ที่ขึ้นต้นด้วยตัวที่สอง ( argv[1]ไปargv[argc - 1]) ตามที่เป็นคำสั่งที่ดำเนินการโดยส่วนใหญ่

²ข้อยกเว้นที่น่าสังเกตว่าการเปิ่นja_JP.SJISสถานระบบ FreeBSD มี charset ไม่มี\มิได้~ตัวละคร!

³โปรดทราบว่าในขณะที่ระบบจำนวนมาก (FreeBSD, Solaris ไม่ใช่ GNU) ให้พิจารณา U + 00A0 [[:blank:]]ในรูปแบบ UTF-8 แต่บางแห่งในสถานที่อื่น ๆ เช่นที่ใช้ ISO8859-15 อาจหลีกเลี่ยงปัญหาประเภทนี้ได้


ในย่อหน้าแรกของคุณคุณบอกเราว่า "... ของตัวละครใน 3 ข้อโต้แย้งที่ส่งผ่านไปยังecho... " ฉันนับแค่ 2 ข้อโต้แย้งที่ถูกส่งผ่านไปยังคำสั่งechoอาร์กิวเมนต์ที่ฉันสามารถนับได้runและafter_bundleสนใจที่จะอธิบายว่าคุณเป็นอย่างไร นับและได้รับ 3 ข้อโต้แย้ง?
Ferrybig

1
@ViktorFonic ดูแก้ไขเกี่ยวกับจำนวนอาร์กิวเมนต์ (และปัญหาหลักไม่ได้อยู่ที่echo) ดู(exec -a foo /bin/echo --help)ในระบบ GNU และด้วยเชลล์ GNU สำหรับวิธีส่งอาร์กิวเมนต์แรกไปยัง/bin/echoยูทิลิตีโดยพลการ
Stéphane Chazelas

@Ferrybig ดูการแก้ไขเชิงอรรถของ Stephane ข้อโต้แย้งในคำสั่งในรูปแบบ C ทั่วไปคืออาร์เรย์ของอาร์กิวเมนต์โดย argv [0] เป็นชื่อปฏิบัติการ ค่อนข้างคล้ายกับ$0และพารามิเตอร์ตำแหน่งในเปลือกหอย
Sergiy Kolodyazhnyy

มีการเข้ารหัส 373 ในอยู่iconvในการที่จะถูกแปลงเป็นESC 'ลอง (เป็นตัวอย่าง):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
ไอแซ

มีการเข้ารหัส 173 ซึ่งในบางจุดโค้ด (นอกเหนือ ESC) 'จะถูกแปลงเป็น ลองprintf '\u2804' | iconv -f utf8 -t BRF | xxdดู มีการเข้ารหัสที่มีจำนวนมากของ codepoints 'ที่กลายเป็น รอบ 8695 codepoints ใน UCS-4 'กลายเป็น ลองprintf '\U627' | iconv -cf utf-8 -t UCS-4ดู หลาย (37) การเข้ารหัสแปลงตัวอักษร 0x127 'ไป ลองprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd
ไอแซ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.