การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck
ผสาน ShellCheck เข้ากับด่านตรวจความปลอดภัย และตีความผลการตรวจสอบเพื่อเสริมความแข็งแกร่งให้ทุกสคริปต์
การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck เป็นบทเรียน DevOps Bootcamp ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน DevOps Bootcamp และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส DevOps Bootcamp มีบทเรียนทั้งหมด 4 บทเรียน
ShellCheck คืออะไรและเหตุใดจึงสำคัญ
ShellCheck เป็นเครื่องมือวิเคราะห์แบบสถิติโอเพนซอร์สสำหรับสคริปต์เชลล์ เครื่องมือนี้วิเคราะห์ซอร์ส Bash (รวมถึง POSIX sh, dash และ ksh) โดยไม่เรียกใช้ แล้วรายงานข้อบกพร่อง โครงสร้างที่ไม่ปลอดภัย ปัญหาความสามารถในการพกพา และปัญหารูปแบบ โดยแต่ละรายการมีรหัสกฎเฉพาะ เช่น SC2086
ในกระบวนการส่งมอบที่เสริมความปลอดภัย ShellCheck ทำหน้าที่เป็น ด่านบังคับ: จะไม่ส่งมอบสคริปต์ใดจนกว่าจะผ่านการตรวจสอบ เรื่องนี้สำคัญเพราะ:
- ช่องโหว่ของเชลล์จำนวนมาก (การแยกคำ การแทรกคำสั่ง การขยายค่าที่ไม่ใส่เครื่องหมายอัญประกาศ) มองไม่เห็นในการทดสอบเส้นทางปกติ แต่จะถูกกระตุ้นเมื่ออินพุตอยู่ภายใต้การควบคุมของผู้โจมตี
- ShellCheck ตรวจพบข้อบกพร่องประเภทเหล่านี้ ก่อนทำงานจริงโดยแทบไม่มีต้นทุน
- เครื่องมืออธิบายว่าแต่ละรูปแบบ เหตุใดจึงอันตราย ทำให้ทีมตระหนักมากขึ้นเมื่อเวลาผ่านไป
ติดตั้งบนระบบใดก็ได้:
# Debian / Ubuntu
sudo apt-get install shellcheck
# macOS (Homebrew)
brew install shellcheck
# From source via Cabal (any platform)
cabal update && cabal install ShellCheck
# Verify
shellcheck --versionเรียกใช้ ShellCheck ครั้งแรก
การเรียกใช้ที่ง่ายที่สุดคือ shellcheck <script> ShellCheck จะอ่านบรรทัด shebang เพื่อระบุรูปแบบของเชลล์ จากนั้นส่งผลการตรวจพบไปยัง stdout
ผลการตรวจพบแต่ละรายการประกอบด้วย:
- ชื่อไฟล์และหมายเลขบรรทัด — ตำแหน่งที่แน่นอน
- ระดับความรุนแรง —
error,warning,infoหรือstyle - รหัส SC — ตัวระบุกฎที่คงที่ ซึ่งคุณสามารถค้นดูหรือปิดใช้งานได้
- คำอธิบายสำหรับมนุษย์ — บอกว่าเกิดข้อผิดพลาด อะไร และมักบอกด้วยว่าควรแก้ไข อย่างไร
เรียกใช้สคริปต์ด้านล่างและสังเกตผลลัพธ์ที่ ShellCheck จะสร้าง:
#!/usr/bin/env bash
# demo_bad.sh — intentionally flawed for ShellCheck demonstration
FILE=$1
if [ $FILE == '' ]; then
echo "No file given"
fi
cat $FILE | grep 'error' | wc -lการตีความผลลัพธ์ ShellCheck และรหัส SC
สำหรับสคริปต์ในฉากก่อนหน้า ShellCheck จะสร้างผลการตรวจพบลักษณะต่อไปนี้:
SC2086(warning) — ใส่เครื่องหมายอัญประกาศคู่เพื่อป้องกันการขยาย glob และการแยกคำ — ที่$FILEใน[ $FILE == '' ]และในcat $FILESC2039/SC3010(info) — == ใน [ ] เป็นส่วนขยายเฉพาะ Bash ให้ใช้ = สำหรับ POSIXSC2002(style) — cat ไม่มีประโยชน์ ลองใช้ cmd < file แทน cat file | cmd
รหัส SC แต่ละรหัสจะเชื่อมโยงไปยังหน้าวิกิที่ https://www.shellcheck.net/wiki/SCxxxx ซึ่งมีเหตุผลประกอบและตัวอย่างที่แก้ไขแล้ว
สคริปต์ฉบับแก้ไข:
#!/usr/bin/env bash
# demo_fixed.sh — ShellCheck-clean
FILE="$1"
if [ -z "$FILE" ]; then
echo "No file given" >&2
exit 1
fi
grep -c 'error' "$FILE"ระดับความรุนแรงและสิ่งที่ควรดำเนินการ
ShellCheck จัดระดับความรุนแรงให้ผลการตรวจพบทุกรายการ ในด่านความปลอดภัย ควรจัดการดังนี้:
- error — เกือบแน่นอนว่าเป็นข้อบกพร่องหรือช่องโหว่ด้านความปลอดภัย บล็อกการสร้าง แก้ไขทันที ตัวอย่าง:
SC2148ไม่มี shebang,SC2070มี$?ที่ไม่ใส่เครื่องหมายอัญประกาศ - warning — รูปแบบที่มีความเสี่ยงสูงและมักถูกใช้โจมตีได้ บล็อกการสร้าง แก้ไขหรือให้เหตุผลอย่างชัดเจนว่าปิดใช้งาน ตัวอย่าง:
SC2086ตัวแปรที่ไม่ใส่เครื่องหมายอัญประกาศ - info — อาจถูกต้องในปัจจุบัน แต่เปราะบางหรือไม่สามารถพกพาได้ แก้ไขใน PR เดียวกัน เว้นแต่จะระบุว่าอยู่นอกขอบเขต
- style — เรื่องความสวยงามหรือแนวทางที่ POSIX แนะนำ แนะนำให้ทำ แต่ไม่บังคับในโค้ดเบสที่เป็น Bash ล้วน
ใช้ --severity=warning เพื่อให้จบการทำงานด้วยสถานะไม่เป็นศูนย์เฉพาะเมื่อมี warning หรือระดับสูงกว่า ซึ่งเป็นเกณฑ์มาตรฐานของด่านความปลอดภัย:
#!/usr/bin/env bash
# gate.sh — fail CI on errors and warnings only
shellcheck --severity=warning scripts/*.sh
echo "ShellCheck exit code: $?"ผสาน ShellCheck เป็นด่านความปลอดภัยของ CI
ด่านความปลอดภัยจะมีประโยชน์ก็ต่อเมื่อเป็นสิ่งที่ บังคับ และ ทำงานอัตโนมัติ รูปแบบด้านล่างจะครอบ ShellCheck ไว้ในขั้นตอน CI ซึ่งจะ:
- ค้นหาไฟล์
.shทุกไฟล์ในคลังโค้ด - เรียกใช้ ShellCheck ด้วย
--severity=warningและผลลัพธ์ JSON ที่เครื่องอ่านได้ - ทำให้กระบวนการส่งมอบล้มเหลว (
exit 1) หากพบผลการตรวจพบรายการใดรายการหนึ่ง - พิมพ์สรุปเพื่อให้วิศวกรดำเนินการกับผลการตรวจพบได้โดยไม่ต้องออกจากบันทึก CI
วางไฟล์นี้ในคลังโค้ดของคุณ แล้วเรียกใช้จากกระบวนการ CI (GitHub Actions, Jenkins, GitLab CI และอื่น ๆ):
#!/usr/bin/env bash
# ci/shellcheck_gate.sh
set -euo pipefail
SCRIPTS=$(find . -name '*.sh' -not -path './.git/*')
FAILED=0
for script in $SCRIPTS; do
echo "==> Checking: $script"
if ! shellcheck --severity=warning --format=tty "$script"; then
FAILED=1
fi
done
if [ "$FAILED" -eq 1 ]; then
echo "[GATE] ShellCheck found warnings or errors. Build blocked." >&2
exit 1
fi
echo "[GATE] All scripts passed ShellCheck."ตระกูล SC2086: การขยายค่าตัวแปรที่ไม่ใส่เครื่องหมายอัญประกาศ
SC2086 เป็นผลการตรวจพบที่พบบ่อยที่สุดของ ShellCheck และเป็นหนึ่งในช่องโหว่ของเชลล์ที่ถูกใช้โจมตีมากที่สุด: การขยายค่าตัวแปรที่ไม่ใส่เครื่องหมายอัญประกาศ
เมื่อไม่ใส่เครื่องหมายอัญประกาศคู่ให้ตัวแปร เชลล์จะดำเนินการ แยกคำ (แยกตาม IFS) และ ขยาย glob กับค่าของตัวแปร ผู้โจมตีที่ควบคุมตัวแปรได้อาจแทรกอาร์กิวเมนต์เพิ่มเติม ทำให้เกิดการไล่สำรวจระบบไฟล์ หรือทำให้คำสั่งได้รับตัวถูกดำเนินการที่ไม่คาดคิด
รูปแบบอันตรายที่พบบ่อย:
#!/usr/bin/env bash
# Attacker sets: FILENAME="important.txt /etc/passwd"
FILENAME="$1"
# UNSAFE — word splitting turns this into two args
rm $FILENAME
# SAFE — double quotes prevent splitting
rm "$FILENAME"
# Arrays are the right tool for lists
FILES=("$@")
rm -- "${FILES[@]}"ตรวจจับความเสี่ยงการแทรกคำสั่งด้วย SC2046 และ SC2035
กฎสำคัญสองข้อที่ไม่ค่อยเป็นที่รู้จักนี้จัดการกับ การแทรกคำสั่งผ่านผลลัพธ์ของเชลล์ย่อย:
SC2046— ใส่เครื่องหมายอัญประกาศเพื่อป้องกันการแยกคำ / glob ภายใน$(…)หากนำผลลัพธ์ของเชลล์ย่อยไปใช้โดยไม่ใส่เครื่องหมายอัญประกาศ อักขระช่องว่างหรือ glob ในผลลัพธ์จะกลายเป็นโทเค็นของเชลล์SC2035— ใช้./*.shแทน*.shเพื่อป้องกันไม่ให้ชื่อไฟล์ที่ขึ้นต้นด้วย-ถูกตีความเป็นตัวเลือก ซึ่งเป็นช่องทางแทรกอาร์กิวเมนต์แบบคลาสสิก
สถานการณ์การโจมตีและวิธีแก้ไขที่เป็นรูปธรรม:
#!/usr/bin/env bash
# SC2046 example — output of find fed unquoted to chmod
# If a filename contains spaces, extra arguments appear
# UNSAFE
chmod 600 $(find /secrets -name '*.key')
# SAFE — use a while-read loop or xargs with -0
find /secrets -name '*.key' -print0 \
| xargs -0 chmod 600
# SC2035 example
# UNSAFE — a file named '-rf' would be passed as an option
rm *.sh
# SAFE
rm -- ./*.shใช้รูปแบบผลลัพธ์ JSON สำหรับการทำงานอัตโนมัติ
ShellCheck รองรับรูปแบบผลลัพธ์หลายแบบผ่าน --format:
tty(ค่าเริ่มต้น) — ผลลัพธ์ในเทอร์มินัลที่มนุษย์อ่านได้json— เครื่องอ่านได้ เหมาะสำหรับแดชบอร์ด ตัวบล็อกแบบกำหนดเอง หรือการอัปโหลดไปยังแพลตฟอร์ม SASTgcc— เข้ากันได้กับเครื่องมือที่วิเคราะห์รูปแบบข้อผิดพลาดของ GCC (IDE, Vim/Emacs)checkstyle— รูปแบบ XML ที่ปลั๊กอิน Checkstyle ของ Jenkins ใช้งาน
รูปแบบ JSON ช่วยให้คุณเขียนนโยบายอัตโนมัติได้ เช่น บล็อกเฉพาะ รหัส SC ที่กำหนด หรือรวบรวมผลการตรวจพบจากโค้ดเบสขนาดใหญ่เป็นรายงานความปลอดภัย
#!/usr/bin/env bash
# Emit JSON and filter for only error-severity findings using jq
shellcheck --format=json scripts/deploy.sh \
| jq '[.[] | select(.level == "error")]'
# Count distinct SC codes across all scripts
find . -name '*.sh' -print0 \
| xargs -0 shellcheck --format=json 2>/dev/null \
| jq '[.[] | .code] | group_by(.) | map({code: .[0], count: length}) | sort_by(-.count)'การปิดผลบวกลวงอย่างถูกต้อง
การปิดใช้งาน ShellCheck แบบครอบคลุมทั้งหมดทำให้จุดประสงค์ของเครื่องมือหมดไป แนวทางที่ถูกต้องคือ การปิดใช้งานแบบเจาะจงพร้อมคำอธิบาย โดยมีผลเฉพาะบรรทัดหรือบล็อกที่แน่ชัดว่าผลการตรวจพบนั้นใช้ไม่ได้จริง
กลไกการปิดใช้งานมีสามแบบ:
- ปิดใช้งานเฉพาะบรรทัด —
# shellcheck disable=SC2086ในบรรทัดเหนือโค้ดที่ทำให้เกิดปัญหา มีผลเฉพาะบรรทัดนั้น - ปิดใช้งาน/เปิดใช้งานเป็นบล็อก — ครอบส่วนหนึ่งด้วย
# shellcheck disable=…และ# shellcheck enable=… - คำสั่งระดับไฟล์ — วาง
# shellcheck disable=…ไว้ด้านบนสุดของไฟล์ (ไม่ค่อยมีเหตุผลเพียงพอ ควรอธิบายสาเหตุ)
การปิดใช้งานทุกครั้ง ต้องมีความคิดเห็นอธิบายว่าเหตุใดผลการตรวจพบจึงเป็นผลบวกลวง:
#!/usr/bin/env bash
# deploy.sh
# Legitimate suppression: $DEPLOY_ARGS is intentionally word-split
# because it is a pre-validated list of flags from a trusted config file.
# shellcheck disable=SC2086
exec deploy-tool $DEPLOY_ARGS
# Block suppression for a section that generates dynamic code
# shellcheck disable=SC2016
VARS='$HOME $PATH $USER'
echo "Unexpanded vars: $VARS"
# shellcheck enable=SC2016กำหนดค่า ShellCheck ผ่าน .shellcheckrc
สำหรับการตั้งค่าทั้งโครงการ ShellCheck จะอ่าน .shellcheckrc จากไดเรกทอรีของสคริปต์ย้อนขึ้นไปจนถึง / วิธีนี้ช่วยให้ไม่ต้องระบุแฟล็กซ้ำทุกครั้งที่เรียกใช้ และทำให้สคริปต์ด่านตรวจเรียบง่าย
คำสั่งที่มีประโยชน์ใน .shellcheckrc:
shell=bash— แทนที่การตรวจหารูปแบบเชลล์ (มีประโยชน์สำหรับไฟล์ที่ไม่มี shebang)enable=all— เปิดใช้การตรวจสอบเพิ่มเติม (เช่นavoid-nullary-conditions,require-variable-braces)disable=SC2059— ปิดใช้งานทั่วทั้งโครงการสำหรับข้อยกเว้นที่มีเหตุผลรองรับexternal-sources=true— ติดตามและตรวจสอบคำสั่งsource/.
# .shellcheckrc — project root
shell=bash
enable=all
external-sources=true
# SC2312: consider invoking this command separately to avoid masking its
# return value — suppressed project-wide because we use set -e.
# Rationale: errexit already aborts on failure; masking risk is mitigated.
disable=SC2312สคริปต์เสริมความปลอดภัยตั้งแต่ต้นจนจบ: ก่อนและหลัง
วิธีที่มีประสิทธิภาพที่สุดในการทำความเข้าใจผลการตรวจพบของ ShellCheck คือการปรับโครงสร้างสคริปต์ที่สมจริง จากสถานะที่ไม่ผ่านไปเป็นสคริปต์ที่สะอาดและเสริมความปลอดภัยแล้ว สคริปต์ด้านล่างสำรองข้อมูลไดเรกทอรีและเขียนขึ้นโดยไม่ได้คำนึงถึงความปลอดภัย โดยไม่ผ่าน ShellCheck อย่างน้อยห้ากฎที่แตกต่างกัน
ศึกษาโค้ดทั้งสองเวอร์ชัน เวอร์ชัน หลังแก้ไข ผ่าน shellcheck --severity=warning โดยไม่มีคำสั่งปิดใช้งาน และปลอดภัยขึ้นอย่างมากเมื่ออินพุตอยู่ภายใต้การควบคุมของผู้โจมตี:
#!/usr/bin/env bash
# BEFORE — multiple ShellCheck violations
DEST=$1
SRC=$2
DATE=`date +%Y%m%d`
if [ ! -d $DEST ]; then
mkdir $DEST
fi
cp -r $SRC $DEST/$DATE
echo Done
#!/usr/bin/env bash
# AFTER — ShellCheck-clean and hardened
set -euo pipefail
DEST="${1:?Usage: backup.sh <dest> <src>}"
SRC="${2:?Usage: backup.sh <dest> <src>}"
DATE=$(date +%Y%m%d)
if [ ! -d "$DEST" ]; then
mkdir -p -- "$DEST"
fi
cp -r -- "$SRC" "$DEST/$DATE"
echo 'Done' >&2ตรวจสอบความรู้: ShellCheck ในด่านความปลอดภัย
ทดสอบความเข้าใจของคุณเกี่ยวกับบทบาทของ ShellCheck ในฐานะด่านความปลอดภัย
ทบทวน: การวิเคราะห์แบบสถิติในฐานะด่านความปลอดภัย
ในบทเรียนนี้ คุณได้เรียนรู้วิธีทำให้ ShellCheck เป็นด่านความปลอดภัยภาคบังคับในกระบวนการทำงาน Bash ของคุณ:
- ShellCheck ทำการวิเคราะห์แบบสถิติโดยไม่เรียกใช้สคริปต์ จึงตรวจจับข้อผิดพลาดด้านเครื่องหมายอัญประกาศ ความเสี่ยงจากการแทรกคำสั่ง และรูปแบบที่ไม่ปลอดภัยได้ก่อนการทำงานจริง
- ผลการตรวจพบทุกรายการมี รหัส SC (เช่น
SC2086) ซึ่งเชื่อมโยงไปยังเอกสารโดยละเอียดและคำแนะนำในการแก้ไข - ลำดับระดับความรุนแรง —
error,warning,info,style— ช่วยให้คุณปรับเกณฑ์ของด่านตรวจได้ โดย--severity=warningเป็นเกณฑ์ความปลอดภัยที่แนะนำ - ใช้ ผลลัพธ์ที่เครื่องอ่านได้ (
--format=json) เพื่อทำรายงาน การติดตามแนวโน้ม และการผสานรวมกับ SAST โดยอัตโนมัติ - ปิดใช้งานอย่างประหยัด: กำหนดเป้าหมายเพียงบรรทัดเดียวเสมอ อธิบายเหตุผลไว้ในความคิดเห็นเสมอ และอย่าปิดใช้งานทั่วโลกเว้นแต่จะมีเหตุผลรองรับใน
.shellcheckrc - ใช้ ShellCheck ร่วมกับ
set -euo pipefailการใส่เครื่องหมายอัญประกาศอย่างชัดเจน ตัวสิ้นสุดอาร์กิวเมนต์-- argumentและการตรวจสอบอินพุต เพื่อเสริมการป้องกันหลายชั้น
สคริปต์ที่ผ่าน ShellCheck ไม่ได้ปลอดภัยโดยอัตโนมัติ แต่สคริปต์ที่ไม่ผ่าน ShellCheck ไม่ควรไปถึงระบบใช้งานจริงโดยเด็ดขาด
คำถามที่พบบ่อย
บทเรียน “การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส DevOps Bootcamp ให้อัปเกรดเป็น CoddyKit PRO คอร์ส DevOps Bootcamp มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck”
ผสาน ShellCheck เข้ากับด่านตรวจความปลอดภัย และตีความผลการตรวจสอบเพื่อเสริมความแข็งแกร่งให้ทุกสคริปต์ คุณปฏิบัติ DevOps Bootcamp ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน DevOps Bootcamp หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน DevOps Bootcamp บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน DevOps Bootcamp นี้ได้ไหม
ได้ บทเรียน DevOps Bootcamp ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การป้องกันการแทรกคำสั่งและอาร์กิวเมนต์
- การจัดการความลับอย่างปลอดภัยและสุขอนามัยของสภาพแวดล้อม
- การทำงานด้วยสิทธิ์น้อยที่สุดและวินัยในการใช้ sudo
- การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck