คำนำ
ก่อนอื่นฉันจะบอกว่ามันไม่ใช่วิธีที่ถูกต้องในการแก้ไขปัญหา มันเหมือนกับว่า " คุณไม่ควรฆ่าคนเพราะไม่เช่นนั้นคุณจะต้องติดคุก "
ในทำนองเดียวกันคุณไม่ได้อ้างตัวแปรของคุณเพราะมิฉะนั้นคุณจะแนะนำช่องโหว่ความปลอดภัย คุณอ้างอิงตัวแปรของคุณเพราะมันไม่ถูกต้องที่จะไม่ (แต่ถ้าความกลัวของคุกสามารถช่วยได้ทำไมไม่)
บทสรุปเล็กน้อยสำหรับผู้ที่เพิ่งกระโดดขึ้นรถไฟ
ในเชลล์ส่วนใหญ่การปล่อยตัวแปรการขยายที่ไม่ได้กล่าวถึง (แม้ว่า (และส่วนที่เหลือของคำตอบนี้) ยังใช้กับการทดแทนคำสั่ง ( `...`
หรือ$(...)
) และการขยายเลขคณิต ( $((...))
หรือ$[...]
) มีความหมายพิเศษมาก วิธีที่ดีที่สุดที่จะอธิบายมันก็คือว่ามันเป็นเหมือนการเรียกการเรียงลำดับของนัยบางแยก + glob operator¹
cmd $var
ในภาษาอื่นจะเขียนเหมือน:
cmd(glob(split($var)))
$var
แบ่งออกเป็นรายการแรกของคำตามกฎซับซ้อนที่เกี่ยวข้องกับ$IFS
พารามิเตอร์พิเศษ ( ส่วนแยก ) แล้วแต่ละคำที่เกิดจากการแยกนั้นถือว่าเป็นรูปแบบที่ขยายไปยังรายการของไฟล์ที่ตรงกับมัน ( ส่วนglob ) .
ตัวอย่างเช่นถ้า$var
มี*.txt,/var/*.xml
และ$IFS
มี,
, cmd
จะเรียกว่ามีจำนวนของการขัดแย้งครั้งแรกเป็นหนึ่งcmd
และคนต่อไปเป็นtxt
ไฟล์ในไดเรกทอรีปัจจุบันและไฟล์ในxml
/var
หากคุณต้องการโทรหาcmd
เพียงแค่สองข้อโต้แย้งcmd
และ*.txt,/var/*.xml
คุณเขียน:
cmd "$var"
ซึ่งจะเป็นภาษาที่คุ้นเคยมากกว่าของคุณ:
cmd($var)
เราหมายถึงอะไรจากความอ่อนแอในเชลล์ ?
ท้ายที่สุดเป็นที่รู้จักกันมาตั้งแต่สมัยก่อนที่เชลล์สคริปต์ไม่ควรใช้ในบริบทที่มีความปลอดภัย แน่นอนว่าการปล่อยตัวแปรที่ไม่ได้อ้างถึงนั้นเป็นข้อผิดพลาด แต่นั่นไม่สามารถทำอันตรายได้มากขนาดนั้นใช่ไหม
ถึงแม้ว่าข้อเท็จจริงที่ว่าไม่มีใครบอกคุณได้ว่าเชลล์สคริปต์ไม่ควรใช้กับ CGIs ของเว็บหรือว่าระบบส่วนใหญ่ไม่อนุญาตให้ใช้สคริปต์เชลล์ setuid / setgid ทุกวันนี้สิ่งหนึ่งที่ shellshock (ข้อผิดพลาดที่หาประโยชน์จากการโจมตีจากระยะไกล พาดหัวข่าวในกันยายน 2014) เปิดเผยว่าเปลือกหอยยังคงมีการใช้อย่างกว้างขวางที่พวกเขาอาจจะไม่ได้ใน CGIs ใน DHCP สคริปต์ลูกค้าเบ็ดในคำสั่ง sudoers อุทธรณ์โดย (ถ้าไม่เป็น ) คำสั่ง setuid ...
บางครั้งก็ไม่รู้ ตัวอย่างเช่นsystem('cmd $PATH_INFO')
ในสคริปต์php
/ perl
/ python
CGI จะเรียกใช้เชลล์เพื่อตีความบรรทัดคำสั่งนั้น (ไม่ต้องพูดถึงข้อเท็จจริงที่ว่าcmd
ตัวเองอาจเป็นเชลล์สคริปต์และผู้เขียนอาจไม่เคยคาดหวังว่ามันจะถูกเรียกจาก CGI)
คุณมีช่องโหว่เมื่อมีเส้นทางสำหรับการเพิ่มสิทธิ์นั่นคือเมื่อมีคน (เรียกเขาว่าผู้โจมตี ) สามารถทำสิ่งที่เขาไม่ได้ตั้งใจ
นั่นหมายความว่าผู้โจมตีให้ข้อมูลข้อมูลที่ถูกประมวลผลโดยผู้ใช้ที่ได้รับสิทธิพิเศษ / กระบวนการซึ่งทำสิ่งที่ไม่ควรทำโดยไม่ได้ตั้งใจในกรณีส่วนใหญ่เนื่องจากข้อผิดพลาด
โดยทั่วไปคุณได้มีปัญหาเมื่อรหัสรถของคุณประมวลผลข้อมูลภายใต้การควบคุมของผู้โจมตี
ตอนนี้ไม่ชัดเจนว่าข้อมูลมาจากไหนและมักจะยากที่จะบอกว่ารหัสของคุณจะได้รับการประมวลผลข้อมูลที่ไม่น่าเชื่อถือหรือไม่
เท่าที่เกี่ยวข้องกับตัวแปรในกรณีของสคริปต์ CGI มันชัดเจนมากข้อมูลคือพารามิเตอร์ CGI GET / POST และสิ่งต่าง ๆ เช่นคุกกี้เส้นทางพารามิเตอร์โฮสต์ ...
สำหรับสคริปต์ setuid (เรียกใช้ในฐานะผู้ใช้รายหนึ่งเมื่อเรียกใช้โดยผู้อื่น) นั่นคืออาร์กิวเมนต์หรือตัวแปรสภาพแวดล้อม
อีกเวกเตอร์ที่พบบ่อยมากคือชื่อไฟล์ หากคุณได้รับรายชื่อไฟล์จากไดเรกทอรีก็เป็นไปได้ว่าไฟล์ที่ได้รับการปลูกโดยมีการโจมตี
ในเรื่องนั้นถึงแม้จะพร้อมต์ของเชลล์แบบโต้ตอบคุณอาจมีช่องโหว่ได้ (เมื่อประมวลผลไฟล์ใน/tmp
หรือ~/tmp
เช่น)
แม้แต่ a ~/.bashrc
สามารถมีความเสี่ยงได้ (ตัวอย่างเช่นbash
จะตีความมันเมื่อถูกเรียกใช้ssh
เพื่อเรียกใช้ForcedCommand
คล้ายในgit
การปรับใช้เซิร์ฟเวอร์ที่มีตัวแปรบางอย่างภายใต้การควบคุมของไคลเอ็นต์)
ตอนนี้สคริปต์อาจไม่ถูกเรียกใช้โดยตรงเพื่อประมวลผลข้อมูลที่ไม่น่าเชื่อถือ แต่อาจถูกเรียกโดยคำสั่งอื่นที่ทำ หรือรหัสที่ไม่ถูกต้องของคุณอาจถูกคัดลอกไปไว้ในสคริปต์ที่ทำ (โดยคุณลง 3 ปีหรือหนึ่งในเพื่อนร่วมงานของคุณ) ที่เดียวที่สำคัญอย่างยิ่งคือคำตอบในเว็บไซต์ถามตอบเพราะคุณจะไม่มีทางรู้ว่าสำเนาของรหัสของคุณจะปรากฏที่ไหน
ลงเพื่อธุรกิจ มันเลวร้ายแค่ไหน?
การปล่อยตัวแปร (หรือการทดแทนคำสั่ง) ที่ไม่ได้กล่าวถึงนั้นเป็นสาเหตุที่ช่องโหว่ด้านความปลอดภัยอันดับหนึ่งที่เกี่ยวข้องกับรหัสเชลล์ ส่วนหนึ่งเป็นเพราะข้อบกพร่องเหล่านั้นมักจะแปลเป็นช่องโหว่ แต่ก็เป็นเพราะมันเป็นเรื่องธรรมดามากที่จะเห็นตัวแปรที่ไม่ได้กล่าวถึง
ที่จริงแล้วเมื่อค้นหาช่องโหว่ในรหัสเชลล์สิ่งแรกที่ต้องทำคือมองหาตัวแปรที่ไม่ได้กล่าวถึง ง่ายต่อการตรวจจับซึ่งมักจะเป็นตัวเลือกที่ดีโดยทั่วไปสามารถติดตามกลับไปยังข้อมูลที่ควบคุมโดยผู้โจมตีได้ง่าย
มีหลายวิธีที่ตัวแปรที่ไม่ได้ระบุไว้สามารถเปลี่ยนเป็นช่องโหว่ได้ ฉันจะให้แนวโน้มทั่วไปไม่กี่ที่นี่
การเปิดเผยข้อมูล
คนส่วนใหญ่จะชนกับข้อผิดพลาดที่เกี่ยวข้องกับตัวแปรที่ไม่ได้อ้างถึงเนื่องจากส่วนแยก (เช่นเป็นเรื่องปกติที่ไฟล์จะมีช่องว่างในชื่อของพวกเขาทุกวันนี้และช่องว่างอยู่ในค่าเริ่มต้นของ IFS) หลายคนจะมองข้าม
ส่วนกลม globส่วนหนึ่งเป็นอย่างน้อยเป็นอันตรายในขณะที่
แยกส่วน
การวนรอบเสร็จสิ้นเมื่ออินพุตภายนอกที่ไม่ได้รับอนุญาตหมายความว่าผู้โจมตีสามารถทำให้คุณอ่านเนื้อหาของไดเรกทอรีใด ๆ
ใน:
echo You entered: $unsanitised_external_input
ถ้า$unsanitised_external_input
มี/*
แสดงว่าผู้โจมตี/
สามารถดูเนื้อหาของ ไม่ใช่เรื่องใหญ่. มันจะกลายเป็นน่าสนใจมากขึ้นแม้ว่าจะมี/home/*
ที่ช่วยให้คุณมีรายชื่อของผู้ใช้บนเครื่อง, /tmp/*
, /home/*/.forward
สำหรับคำแนะนำในการปฏิบัติที่เป็นอันตรายอื่น ๆ/etc/rc*/*
สำหรับการให้บริการที่เปิดใช้งาน ... ไม่จำเป็นต้องตั้งชื่อพวกเขาเป็นรายบุคคล ค่า/*
/*/* /*/*/*...
จะแสดงรายการระบบไฟล์ทั้งหมด
การปฏิเสธช่องโหว่บริการ
ทำให้กรณีก่อนหน้านี้ไกลไปหน่อยและเรามี DoS
ที่จริงแล้วตัวแปรใด ๆ ที่ไม่ได้กล่าวถึงในบริบทรายการที่มีอินพุตที่ไม่ได้รับการแก้ไขเป็นอย่างน้อยช่องโหว่ DoS
แม้แต่ผู้เชี่ยวชาญเชลล์สแครเตอร์ทั่วไปก็มักจะลืมพูดสิ่งต่าง ๆ เช่น:
#! /bin/sh -
: ${QUERYSTRING=$1}
:
เป็นคำสั่ง no-op สิ่งที่อาจจะผิดไป?
นั่นหมายถึงการกำหนด$1
ให้$QUERYSTRING
ถ้าไม่$QUERYSTRING
ได้ตั้งค่า นั่นเป็นวิธีที่รวดเร็วในการทำให้สคริปต์ CGI สามารถเรียกใช้จากบรรทัดคำสั่งได้เช่นกัน
ที่$QUERYSTRING
ยังคงขยายแม้ว่าและเนื่องจากยังไม่ได้อ้างถึงตัวดำเนินการแยก + globจะถูกเรียกใช้
ตอนนี้มี globs บางอย่างที่มีราคาแพงโดยเฉพาะอย่างยิ่งที่จะขยาย สิ่ง/*/*/*/*
หนึ่งนั้นไม่ดีพอเพราะมันหมายถึงการแสดงรายการไดเรกทอรีได้ถึง 4 ระดับ นอกเหนือจากกิจกรรมของดิสก์และ CPU นั่นหมายถึงการจัดเก็บเส้นทางไฟล์นับหมื่น (40k ที่นี่บนเซิร์ฟเวอร์ขนาดเล็ก VM, 10k ของไดเรกทอรีที่)
ตอนนี้/*/*/*/*/../../../../*/*/*/*
หมายถึง 40k x 10k และ
/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*
เพียงพอที่จะนำเครื่องที่ทรงพลังที่สุดมาคุกเข่า
ลองด้วยตัวคุณเอง (แม้ว่าจะเตรียมไว้ให้เครื่องของคุณพังหรือแฮงค์):
a='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*' sh -c ': ${a=foo}'
แน่นอนถ้ารหัสคือ:
echo $QUERYSTRING > /some/file
จากนั้นคุณสามารถเติมดิสก์
เพียงค้นหา google บนshell cgiหรือbash cgiหรือksh cgiแล้วคุณจะพบหน้าไม่กี่หน้าที่แสดงวิธีเขียน CGIs ใน shells สังเกตว่าครึ่งหนึ่งของพารามิเตอร์กระบวนการนั้นมีความเสี่ยงอย่างไร
แม้แต่ตัวของเดวิดกรก็
ยังอ่อนไหว (ดูที่การจัดการคุกกี้)
มากถึงจุดอ่อนในการใช้รหัสโดยอำเภอใจ
การใช้รหัสโดยอำเภอใจเป็นช่องโหว่ประเภทที่แย่ที่สุดเนื่องจากหากผู้โจมตีสามารถเรียกใช้คำสั่งใด ๆ ได้ไม่มีข้อ จำกัด ในสิ่งที่เขาอาจทำ
โดยทั่วไปแล้วจะเป็นส่วนแยกที่นำไปสู่ การแยกนั้นส่งผลให้เกิดอาร์กิวเมนต์หลายตัวที่จะถูกส่งผ่านไปยังคำสั่งเมื่อคาดว่าจะมีเพียงหนึ่งตัวเท่านั้น ในขณะที่คนแรกของคนเหล่านั้นจะถูกนำมาใช้ในบริบทที่คาดหวังคนอื่น ๆ จะอยู่ในบริบทที่แตกต่างกันดังนั้นอาจตีความแตกต่างกัน ดีขึ้นด้วยตัวอย่าง:
awk -v foo=$external_input '$2 == foo'
ที่นี่มีจุดประสงค์เพื่อกำหนดเนื้อหาของ
$external_input
ตัวแปรเชลล์ให้กับfoo
awk
ตัวแปร
ขณะนี้:
$ external_input='x BEGIN{system("uname")}'
$ awk -v foo=$external_input '$2 == foo'
Linux
คำที่สองที่เกิดจากการแยกของ$external_input
ไม่ได้มอบหมายให้foo
แต่ถือว่าเป็นawk
รหัส (ที่นี่ที่รันคำสั่งโดยพล: uname
)
นั่นเป็นปัญหาโดยเฉพาะอย่างยิ่งสำหรับคำสั่งที่สามารถรันคำสั่งอื่น ๆ ( awk
, env
, sed
(GNU หนึ่ง) perl
, find
... ) โดยเฉพาะอย่างยิ่งกับสายพันธุ์ของกนู (ซึ่งยอมรับตัวเลือกหลังจากมีปากเสียง) บางครั้งคุณจะไม่สงสัยว่าคำสั่งเพื่อให้สามารถดำเนินการอื่น ๆ เช่นksh
, bash
หรือzsh
's [
หรือprintf
...
for file in *; do
[ -f $file ] || continue
something-that-would-be-dangerous-if-$file-were-a-directory
done
หากเราสร้างไดเรกทอรีที่เรียกว่าx -o yes
การทดสอบจะเป็นค่าบวกเนื่องจากเป็นนิพจน์เงื่อนไขที่แตกต่างอย่างสิ้นเชิงที่เรากำลังประเมิน
ถ้าเราสร้างไฟล์ที่เรียกว่าx -a a[0$(uname>&2)] -gt 1
ด้วยการใช้งาน ksh ทั้งหมดอย่างน้อย (ซึ่งรวมถึงsh
Unices เชิงพาณิชย์ส่วนใหญ่และ BSD บางส่วน) ที่ดำเนินการuname
เพราะเชลล์เหล่านั้นทำการประเมินทางคณิตศาสตร์กับตัวดำเนินการเปรียบเทียบเชิงตัวเลขของ[
คำสั่ง
$ touch x 'x -a a[0$(uname>&2)] -gt 1'
$ ksh -c 'for f in *; do [ -f $f ]; done'
Linux
เช่นเดียวกันกับสำหรับชื่อไฟล์เช่นbash
x -a -v a[0$(uname>&2)]
แน่นอนว่าหากพวกเขาไม่สามารถดำเนินการตามอำเภอใจได้ผู้โจมตีอาจตัดสินความเสียหายที่น้อยลง (ซึ่งอาจช่วยในการเรียกใช้งานโดยพลการ) คำสั่งใด ๆ ที่สามารถเขียนไฟล์หรือเปลี่ยนสิทธิ์การเป็นเจ้าของหรือมีผลกระทบหลักหรือผลข้างเคียงใด ๆ ที่อาจถูกนำมาใช้
ทุกสิ่งสามารถทำได้ด้วยชื่อไฟล์
$ touch -- '-R ..'
$ for file in *; do [ -f "$file" ] && chmod +w $file; done
และคุณก็สามารถ..
เขียนได้ (ซ้ำกับ GNU
chmod
)
สคริปต์ที่ทำการประมวลผลไฟล์โดยอัตโนมัติในพื้นที่ที่สามารถเขียนได้แบบสาธารณะ/tmp
ต้องเขียนอย่างระมัดระวัง
เกี่ยวกับอะไร [ $# -gt 1 ]
นั่นคือสิ่งที่ฉันพบว่าทำให้โมโห บางคนลงไปด้วยความสงสัยทั้งหมดว่าการขยายตัวแบบใดแบบหนึ่งอาจเป็นปัญหาในการตัดสินใจว่าพวกเขาสามารถละเว้นคำพูด
มันก็เหมือนกับการพูด เฮ้ดูเหมือนว่า$#
ไม่สามารถจะเป็นเรื่องที่ผู้ประกอบการแยก + glob ขอถามเปลือกแยก + glob มัน หรือเฮ้ขอเขียนรหัสไม่ถูกต้องเพียงเพราะข้อผิดพลาดที่ไม่น่าจะได้รับผลกระทบ
ตอนนี้มันเป็นไปได้ยังไง ตกลง, $#
(หรือ$!
, $?
หรือการแทนที่เลขคณิตใด ๆ ) อาจมีเฉพาะตัวเลข (หรือ-
สำหรับบางส่วน) ดังนั้นส่วนglobจะออก เพื่อแยกส่วนที่จะทำอะไรบางอย่าง แต่ทั้งหมดที่เราต้องการคือ$IFS
การมีตัวเลข (หรือ-
)
ด้วยกระสุนบางตัว$IFS
อาจได้รับมาจากสภาพแวดล้อม แต่ถ้าสภาพแวดล้อมไม่ปลอดภัยมันก็จบลงแล้ว
ตอนนี้ถ้าคุณเขียนฟังก์ชั่นเช่น:
my_function() {
[ $# -eq 2 ] || return
...
}
หมายความว่าพฤติกรรมของฟังก์ชันของคุณขึ้นอยู่กับบริบทที่เรียกว่า หรือกล่าวอีกนัย$IFS
หนึ่งกลายเป็นหนึ่งในอินพุตของมัน พูดอย่างเคร่งครัดเมื่อคุณเขียนเอกสาร API สำหรับฟังก์ชั่นของคุณควรเป็นดังนี้:
# my_function
# inputs:
# $1: source directory
# $2: destination directory
# $IFS: used to split $#, expected not to contain digits...
และการเรียกใช้รหัสของคุณต้องทำให้แน่ใจว่า$IFS
ไม่มีตัวเลข ทั้งหมดนั่นเป็นเพราะคุณไม่รู้สึกอยากพิมพ์ตัวละครสองตัว
ตอนนี้สำหรับ[ $# -eq 2 ]
ข้อผิดพลาดที่จะกลายเป็นช่องโหว่ที่คุณจะต้องอย่างใดสำหรับค่าของ$IFS
ที่จะกลายเป็นภายใต้การควบคุมของผู้โจมตี เป็นไปได้ว่าปกติแล้วจะไม่เกิดขึ้นเว้นแต่ว่าผู้โจมตีจัดการเพื่อใช้ประโยชน์จากข้อผิดพลาดอื่น
แต่นั่นก็ไม่เคยได้ยินมาก่อน กรณีทั่วไปคือเมื่อคนลืมที่จะฆ่าเชื้อข้อมูลก่อนที่จะใช้ในการแสดงออกทางคณิตศาสตร์ เราได้เห็นแล้วข้างต้นว่ามันสามารถอนุญาตให้มีการใช้รหัสโดยอำเภอใจในเชลล์บางตัว แต่ในทั้งหมดนั้นมันอนุญาตให้
ผู้โจมตีให้ค่าจำนวนเต็มใด ๆ กับตัวแปร
ตัวอย่างเช่น
n=$(($1 + 1))
if [ $# -gt 2 ]; then
echo >&2 "Too many arguments"
exit 1
fi
และด้วย$1
ค่า(IFS=-1234567890)
ที่มีการประเมินผลเลขคณิตนั้นมีผลข้างเคียงของการตั้งค่า IFS และ[
คำสั่งถัดไปล้มเหลวซึ่งหมายความว่าการตรวจสอบargs จำนวนมากเกินไปจะถูกข้าม
จะเกิดอะไรขึ้นเมื่อไม่มีการเรียกใช้ตัวดำเนินการแยก + glob
มีอีกกรณีหนึ่งที่จำเป็นต้องใช้เครื่องหมายคำพูดรอบตัวแปรและส่วนขยายอื่น ๆ : เมื่อใช้เป็นรูปแบบ
[[ $a = $b ]] # a `ksh` construct also supported by `bash`
case $a in ($b) ...; esac
ไม่ได้ทดสอบว่า$a
และ$b
จะเหมือนกัน (ยกเว้นด้วยzsh
) แต่ถ้าตรงกับรูปแบบใน$a
$b
และคุณจำเป็นต้องพูด$b
ถ้าคุณต้องการที่จะเปรียบเทียบเป็นสตริง (สิ่งเดียวกันใน"${a#$b}"
หรือ"${a%$b}"
หรือ"${a##*$b*}"
ที่$b
ควรจะยกมาถ้าไม่ได้ที่จะนำมาเป็นรูปแบบ)
สิ่งที่หมายถึงคือว่า[[ $a = $b ]]
อาจจะกลับมาเป็นจริงในกรณีที่$a
มีความแตกต่างจาก$b
(เช่นเมื่อ$a
เป็นanything
และ$b
เป็น*
) หรืออาจจะกลับมาเป็นเท็จเมื่อพวกเขาอยู่เหมือนกัน (ตัวอย่างเช่นเมื่อทั้งสอง$a
และ$b
มี[a]
)
สามารถทำให้เกิดช่องโหว่ด้านความปลอดภัยได้หรือไม่? ใช่เช่นข้อผิดพลาดใด ๆ ที่นี่ผู้โจมตีสามารถเปลี่ยนแปลงลอจิกโค้ดของสคริปต์และ / หรือทำลายสมมติฐานที่สคริปต์ของคุณกำลังทำอยู่ ตัวอย่างเช่นด้วยรหัสเช่น:
if [[ $1 = $2 ]]; then
echo >&2 '$1 and $2 cannot be the same or damage will incur'
exit 1
fi
ผู้โจมตี'[a]' '[a]'
สามารถหลีกเลี่ยงการตรวจสอบโดยการส่งผ่าน
ทีนี้ถ้าไม่มีการจับคู่รูปแบบนั้นหรือโอเปอเรเตอร์+ glob ที่แยกกันจะนำไปใช้สิ่งใดที่เป็นอันตรายต่อการปล่อยตัวแปรไว้โดยไม่ต้องสอบถาม
ฉันต้องยอมรับว่าฉันเขียน:
a=$b
case $a in...
การพูดไม่เป็นอันตราย แต่ไม่จำเป็นอย่างเคร่งครัด
อย่างไรก็ตามผลกระทบด้านหนึ่งของคำพูดที่ถนัดในกรณีดังกล่าว (เช่นใน Q & A ตอบ) ก็คือว่ามันสามารถส่งข้อความที่ผิดที่จะเริ่มต้น: ว่ามันอาจจะทั้งหมดสิทธิที่จะไม่พูดตัวแปร
ตัวอย่างเช่นพวกเขาอาจจะเริ่มคิดว่าถ้าa=$b
มีการตกลงแล้วexport a=$b
จะเป็นเช่นเดียว (ซึ่งก็ไม่ได้อยู่ในเปลือกหอยจำนวนมากเป็นมันในการขัดแย้งกับexport
คำสั่งดังนั้นในบริบทรายการ) env a=$b
หรือ
เกี่ยวกับzsh
อะไร
zsh
แก้ไขข้อผิดพลาดส่วนใหญ่ของการออกแบบเหล่านั้นได้ ในzsh
(อย่างน้อยเมื่อไม่ได้อยู่ในโหมดการจำลอง sh / ksh) หากคุณต้องการแยกหรือกลมหรือการจับคู่รูปแบบคุณต้องขออย่างชัดเจน: $=var
เพื่อแยกและ$~var
กลมหรือเนื้อหาของตัวแปรที่จะถือว่าเป็น รูปแบบ
อย่างไรก็ตามการแยก (แต่ไม่ใช่ globbing) ยังคงดำเนินการโดยปริยายเมื่อมีการทดแทนคำสั่งที่ไม่ได้อ้างอิง (เหมือนในecho $(cmd)
)
นอกจากนี้ยังมีผลข้างเคียงที่ไม่พึงประสงค์บางครั้งไม่ quoting ตัวแปรคือการกำจัดเปล่า zsh
พฤติกรรมคล้ายกับสิ่งที่คุณสามารถประสบความสำเร็จในเปลือกหอยอื่น ๆ โดยการปิด globbing ทั้งหมด (กับset -f
) และแยก (กับIFS=''
) ยังคงอยู่ใน:
cmd $var
จะไม่มีการแบ่ง + globแต่ถ้า$var
ว่างเปล่าแทนที่จะรับอาร์กิวเมนต์ว่างเปล่าหนึ่งรายการcmd
จะไม่ได้รับอาร์กิวเมนต์เลย
ที่สามารถทำให้เกิดข้อบกพร่อง (เช่นชัดเจน[ -n $var ]
) ที่อาจทำลายความคาดหวังและข้อสันนิษฐานของสคริปต์และทำให้เกิดช่องโหว่ แต่ตอนนี้ฉันไม่สามารถสร้างตัวอย่างที่ไม่สามารถดึงมาได้ไกลเกินไป)
สิ่งที่เกี่ยวกับเมื่อคุณทำต้องแยก + globผู้ประกอบการ?
ใช่นั่นคือโดยทั่วไปเมื่อคุณไม่ต้องการให้ตัวแปรไม่อยู่ในเครื่องหมายคำพูด แต่จากนั้นคุณต้องตรวจสอบให้แน่ใจว่าคุณปรับตัวแยกและตัวดำเนินการแบบหมุนได้อย่างถูกต้องก่อนใช้งาน หากคุณต้องการเพียงแยกส่วนและไม่globส่วนหนึ่ง (ซึ่งเป็นกรณีที่ใช้เวลาส่วนใหญ่) แล้วคุณจะต้องปิดการใช้งาน globbing ( set -o noglob
/ set -f
) $IFS
และแก้ไข มิฉะนั้นคุณจะทำให้เกิดช่องโหว่ได้เช่นกัน (เช่นตัวอย่าง CGI ของ David Korn ที่กล่าวถึงข้างต้น)
ข้อสรุป
กล่าวโดยสรุปการทิ้งตัวแปร (หรือการทดแทนคำสั่งหรือการขยายเลขคณิต) ที่ไม่ได้กล่าวถึงในเชลล์อาจเป็นอันตรายอย่างยิ่งโดยเฉพาะอย่างยิ่งเมื่อทำในบริบทที่ไม่ถูกต้องและยากที่จะทราบว่าบริบทเหล่านั้นผิด
นั่นเป็นหนึ่งในเหตุผลที่ว่าทำไมมันจะถือปฏิบัติไม่ดี
ขอบคุณที่อ่านจนถึงตอนนี้ ถ้ามันไปเหนือหัวของคุณไม่ต้องกังวล ไม่มีใครคาดหวังให้ทุกคนเข้าใจถึงความหมายทั้งหมดของการเขียนโค้ดในแบบที่พวกเขาเขียน นั่นเป็นเหตุผลที่เรามี
คำแนะนำการปฏิบัติที่ดีเพื่อให้พวกเขาสามารถปฏิบัติตามได้โดยไม่จำเป็นต้องเข้าใจว่าทำไม
(และในกรณีที่ยังไม่ชัดเจนโปรดหลีกเลี่ยงการเขียนรหัสความปลอดภัยในเชลล์)
และโปรดอ้างอิงตัวแปรในคำตอบของคุณในเว็บไซต์นี้!
¹ในksh93
และpdksh
และสัญญาซื้อขายล่วงหน้า, การขยายรั้งจะดำเนินการด้วยเว้นแต่ว่า globbing ถูกปิดใช้งาน (ในกรณีของksh93
รุ่นที่สูงถึง ksh93u +, แม้ว่าbraceexpand
ตัวเลือกถูกปิดใช้งาน)