0Pricing
DevOps Bootcamp · บทเรียน

การวิเคราะห์แบบสถิตและการตรวจสอบด้วย 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 $FILE
  • SC2039 / SC3010 (info) — == ใน [ ] เป็นส่วนขยายเฉพาะ Bash ให้ใช้ = สำหรับ POSIX
  • SC2002 (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 — เครื่องอ่านได้ เหมาะสำหรับแดชบอร์ด ตัวบล็อกแบบกำหนดเอง หรือการอัปโหลดไปยังแพลตฟอร์ม SAST
  • gcc — เข้ากันได้กับเครื่องมือที่วิเคราะห์รูปแบบข้อผิดพลาดของ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การป้องกันการแทรกคำสั่งและอาร์กิวเมนต์
  2. การจัดการความลับอย่างปลอดภัยและสุขอนามัยของสภาพแวดล้อม
  3. การทำงานด้วยสิทธิ์น้อยที่สุดและวินัยในการใช้ sudo
  4. การวิเคราะห์แบบสถิตและการตรวจสอบด้วย ShellCheck
← กลับไปที่ DevOps Bootcamp