0Pricing
DevOps Bootcamp · درس

منع حقن الأوامر والوسائط

استخدم الاقتباس والتحقّق وتمرير المداخل غير الموثوقة كمصفوفات للقضاء على تقسيم الكلمات والحقن المعتمد على eval

منع حقن الأوامر والوسائط درس مجاني في DevOps Bootcamp على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في DevOps Bootcamp، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.

لماذا تحدث هجمات الحقن في Bash

إن Bash لغة ربط قوية — فهي تمرر النص مباشرةً إلى النواة والبرامج والصدفات الفرعية. وتتحول هذه القوة إلى نقطة ضعف بمجرد وصول إدخال غير موثوق إلى أمر من دون التحقق منه أو وضعه بين علامات اقتباس.

هناك سببان أساسيان يقفان وراء معظم هجمات الحقن في Bash:

  • تقسيم الكلمات: تُقسَّم المتغيرات غير الموضوعة بين علامات اقتباس عند محارف المسافات البيضاء (IFS)، فتحوّل قيمة منطقية واحدة إلى عدة رموز للصدفة.
  • توسيع الأنماط العامة: توسّع الصدفة محارف مثل * و? و[ قبل تنفيذ الأمر حتى.

يمكن لمهاجم يتحكم في اسم ملف أو اسم مستخدم أو مَعلمة URL أو متغير بيئة استغلال الأمرين معًا لتنفيذ أوامر عشوائية أو قراءة الملفات أو تصعيد الامتيازات.

يوضح هذا الدرس بدقة كيفية ظهور هذه الثغرات، والأهم من ذلك، كيفية التخلص منها باستخدام الاقتباس الصحيح والتحقق من الإدخال وتمرير الوسائط باستخدام المصفوفات.

تقسيم الكلمات: التهديد الصامت

عندما يرى Bash متغيرًا غير موضوع بين علامات اقتباس، فإنه يقسم قيمته عند أي محرف مُدرج في $IFS (القيمة الافتراضية: المسافة وعلامة الجدولة والسطر الجديد). وهكذا تتحول قيمة تبدو كأنها وسيط واحد إلى عدة وسائط.

شغّل البرنامج النصي أدناه ولاحظ كيف يتحول اسم ملف يحتوي على مسافة إلى وسيطين منفصلين بالنسبة إلى rm.

#!/usr/bin/env bash
# Dangerous: unquoted variable
FILE='important file.txt'

# Create the file so the demo is self-contained
touch "$FILE"

echo "Files before:"
ls

# BUG: rm sees TWO arguments: 'important' and 'file.txt'
# If 'important' does not exist, rm prints an error but continues.
rm $FILE   # <-- unquoted, word-split happens here

echo "Files after (unquoted rm):"
ls

استخدم علامات الاقتباس دائمًا: القاعدة الأولى لكتابة Bash آمن

إن أبسط وسيلة دفاع وأكثرها فعالية ضد تقسيم الكلمات هي إحاطة توسعات المتغيرات بعلامتي اقتباس مزدوجتين دائمًا.

  • "$var" — تتوسع إلى رمز واحد تمامًا، مع الحفاظ على المسافات وعلامات الجدولة والأسطر الجديدة.
  • 'literal' — علامات اقتباس مفردة: لا تُجرى أي توسعات، وهي مفيدة للسلاسل الثابتة.
  • لا تستخدم $var من دون اقتباس إلا إذا كنت تحتاج صراحةً إلى تقسيم الكلمات وتوسيع الأنماط العامة.

يوضح البرنامج النصي أدناه النسخة الآمنة من المثال السابق.

#!/usr/bin/env bash
set -euo pipefail

FILE='important file.txt'
touch "$FILE"

echo 'Files before:'
ls

# SAFE: double-quotes keep the filename as one token
rm "$FILE"

echo 'Files after (quoted rm):'
ls

حقن الأنماط العامة: عندما تصبح * سلاحًا

تخضع المتغيرات غير الموضوعة بين علامات اقتباس أيضًا لـتوسيع أسماء المسارات (الأنماط العامة). فإذا احتوى الإدخال الذي يتحكم فيه المستخدم على * أو ?، يوسّعه Bash وفقًا لنظام الملفات قبل تنفيذ الأمر.

مثال تقليدي على مسار هجوم: نموذج ويب يعيّن PATTERN=*، ثم يشغّل البرنامج النصي الأمر cp $PATTERN /tmp/leak/، ما يؤدي إلى نسخ كل ملف في الدليل الحالي.

الإصلاح هو نفسه: إحاطة المتغير بعلامتي اقتباس مزدوجتين. إذ تُمرَّر "$PATTERN" الموضوعة بين علامتي اقتباس حرفيًا، ولا تُجري الصدفة توسيعًا للأنماط العامة عليها.

#!/usr/bin/env bash
set -euo pipefail

# Simulate attacker-supplied input
PATTERN='*'

mkdir -p /tmp/safe_demo_src /tmp/safe_demo_dst
touch /tmp/safe_demo_src/secret1.txt /tmp/safe_demo_src/secret2.txt

cd /tmp/safe_demo_src

# UNSAFE: glob expands, copies every file
# cp $PATTERN /tmp/safe_demo_dst/

# SAFE: pattern is treated as a literal filename
cp "$PATTERN" /tmp/safe_demo_dst/ 2>&1 || echo 'No file named literally "*" — attack neutralised'

rm -rf /tmp/safe_demo_src /tmp/safe_demo_dst

حقن الوسائط عبر المعاملات الموضعية غير الموضوعة بين علامات اقتباس

تُعدّ البرامج النصية التي تقبل وسائط من المستدعي أهدافًا مثالية للحقن. يجب وضع كل معامل موضعي ($1 و$2 ...) بين علامات اقتباس في كل موضع يُستخدم فيه.

ومن الأنماط الخطيرة بصفة خاصة تمرير $@ أو $* من دون اقتباس إلى أمر آخر:

  • "$@" — يوسّع كل معامل موضعي إلى كلمة منفصلة وموضوعة بين علامات اقتباس بشكل مستقل. استخدم هذا الشكل دائمًا.
  • $@ أو $* من دون اقتباس — يخضعان لتقسيم الكلمات وتوسيع الأنماط العامة.
  • "$*" — يضم جميع المعاملات في كلمة واحدة (وهو ما نادرًا ما تريده).
#!/usr/bin/env bash
set -euo pipefail

# Safe wrapper: forward all arguments quoted
grep_wrapper() {
    local pattern="$1"
    shift
    # "$@" preserves each file argument as one token
    grep -rn "$pattern" "$@"
}

# Usage: ./script 'error msg' /var/log/syslog '/path with spaces/app.log'
echo 'Searching current script for "safe":'
grep_wrapper 'safe' "$0"

حقن الأوامر باستخدام eval والإدخال غير المتحقق منه

يعيد eval تحليل وسيطه باعتباره شفرة للصدفة. ويمكن لأي بيانات غير موثوقة تصل إلى eval تنفيذ أوامر عشوائية.

الأنماط الخطيرة الشائعة:

  • eval "$user_input"
  • eval echo \$$var (البحث غير المباشر عن المتغير)
  • تمرير بيانات المستخدم عبر bash -c "$input"

القاعدة: لا تمرر أبدًا إدخالًا غير موثوق إلى eval أو bash -c. استخدم بدائل Bash الآمنة:

  • التوسعة غير المباشرة: ${!varname} بدلًا من eval echo \$$varname
  • المصفوفات الترابطية للبحث الديناميكي عن أزواج المفتاح والقيمة
  • الدوال بدلًا من سلاسل الأوامر المُنشأة
#!/usr/bin/env bash
set -euo pipefail

# Simulated attacker-supplied variable name
VARNAME='PATH; echo INJECTED'

# UNSAFE: eval lets attacker run 'echo INJECTED'
# eval "echo \$$VARNAME"

# SAFE: indirect expansion only resolves valid variable names
# First validate that VARNAME is a legal identifier
if [[ "$VARNAME" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
    echo "Value: ${!VARNAME}"
else
    echo "ERROR: invalid variable name: '$VARNAME'" >&2
    exit 1
fi

التحقق من الإدخال: قوائم السماح أفضل من قوائم المنع

إن رفض المحارف المعروفة بأنها ضارة (أي قائمة المنع) أسلوب هش؛ إذ يستطيع المهاجمون العثور على ترميزات أو محارف لم تضعها في الحسبان. بدلًا من ذلك، استخدم قائمة سماح: اقبل المحارف التي تعرف أنها آمنة فقط.

استراتيجيات قوائم السماح في Bash:

  • مطابقة التعبير النمطي: [[ "$input" =~ ^[A-Za-z0-9_-]+$ ]]
  • مطابقة النمط: case "$input" in [A-Za-z0-9]*) ... ;; esac
  • التحقق من التعداد: قارن القيمة بمجموعة ثابتة من القيم الصالحة

تحقق من الإدخال عند الحد الفاصل — فور دخوله إلى البرنامج النصي — وقبل أن يصل إلى أي أمر.

#!/usr/bin/env bash
set -euo pipefail

validate_username() {
    local name="$1"
    # Allowlist: only lowercase letters, digits, underscore, hyphen; 1-32 chars
    if [[ ! "$name" =~ ^[a-z0-9_-]{1,32}$ ]]; then
        echo "ERROR: invalid username '${name}'" >&2
        return 1
    fi
    echo "Username accepted: $name"
}

validate_username 'alice'          # OK
validate_username 'bob_smith-2'    # OK
validate_username 'root; rm -rf /' # REJECTED
validate_username '../etc/passwd'   # REJECTED

استخدام المصفوفات لتمرير الوسائط بأمان

عندما تحتاج إلى إنشاء أمر ديناميكيًا — مثل إضافة الخيارات بصورة شرطية أو التكرار على المدخلات — استخدم مصفوفة Bash بدلًا من دمج السلاسل النصية.

يؤدي دمج السلاسل إلى طمس بنيتها بالكامل، بينما تحافظ مصفوفة Bash على كل وسيط بوصفه عنصرًا مستقلًا من دون إعادة تحليل الصدفة.

  • التعريف: args=()
  • الإضافة: args+=(--flag "$value")
  • التنفيذ: command "${args[@]}"

توسّع "${args[@]}" كل عنصر بوصفه كلمة منفصلة وموضوعة بين علامات اقتباس بشكل مستقل — تمامًا مثل "$@".

#!/usr/bin/env bash
set -euo pipefail

# Build a find command safely with an array
BASEDIR='/tmp'
USER_PATTERN='*.log'   # could come from user input (validate first!)
MAX_DAYS=7

cmd=(find "$BASEDIR" -type f -name "$USER_PATTERN" -mtime "+${MAX_DAYS}")

# Optionally add -delete only when requested
DELETE=false
if [[ "$DELETE" == 'true' ]]; then
    cmd+=(-delete)
fi

echo "Running: ${cmd[*]}"
"${cmd[@]}"

الفاصل --: الحماية من حقن الخيارات

حتى الوسيط الموضوع بين علامات الاقتباس بشكل صحيح يمكن تفسيره باعتباره خيارًا إذا بدأ بـ -. تأمل rm "$file" عندما تكون قيمة file='-rf .': تحمي علامات الاقتباس من تقسيم الكلمات، لكن rm لا يزال يفسر -rf على أنه خيارات.

يشير اصطلاح POSIX -- إلى نهاية الخيارات في معظم الأدوات المساعدة من GNU وBSD. ويُعامل كل ما يأتي بعد -- على أنه وسيط موضعي، وليس خيارًا مطلقًا.

#!/usr/bin/env bash
set -euo pipefail

# Simulate a dangerous filename supplied by the user
FILENAME='-rf /tmp/safe_demo_target'

mkdir -p /tmp/safe_demo_target
touch /tmp/safe_demo_target/keep_me.txt

echo 'Files before:'
ls /tmp/safe_demo_target/

# UNSAFE (flag injection — do NOT uncomment in real systems):
# rm "$FILENAME"

# SAFE: -- ends option processing; filename is treated literally
rm -- "$FILENAME" 2>&1 || echo "No such file (attack neutralised): $FILENAME"

echo 'Files after:'
ls /tmp/safe_demo_target/
rm -rf /tmp/safe_demo_target

تنقية الإدخال لاستخدامه مع SQL والأدوات الخارجية

عندما تستدعي البرامج النصية في Bash واجهات سطر أوامر قواعد البيانات (psql وmysql) أو curl مع عناوين URL يحددها المستخدم، أو أدوات مشابهة، تنطبق طبقتان إضافيتان:

  • الاستعلامات ذات المعاملات: لا تُدرج بيانات المستخدم أبدًا داخل سلاسل SQL. مرّر القيم عبر -v في psql أو --data-urlencode في curl.
  • افصل البيانات عن الشفرة: استخدم printf مع سلسلة تنسيق حرفية؛ ولا تسمح أبدًا بأن يكون إدخال المستخدم هو سلسلة التنسيق.

يستعلم المثال أدناه من PostgreSQL بأمان، مع إبقاء القيمة التي قدمها المستخدم خارج نص SQL تمامًا.

#!/usr/bin/env bash
set -euo pipefail

# Validate first: only allow alphanumeric usernames
USERNAME="${1:-alice}"
if [[ ! "$USERNAME" =~ ^[a-z0-9_]{1,32}$ ]]; then
    echo 'ERROR: invalid username' >&2
    exit 1
fi

# UNSAFE:
# psql -c "SELECT * FROM users WHERE name = '$USERNAME';"

# SAFE: pass value as a psql variable, never inside the SQL text
# psql -v username="$USERNAME" -c 'SELECT * FROM users WHERE name = :username;'

# Demonstrate the principle with printf (safe format string usage)
printf 'Query would use username: %s\n' "$USERNAME"

قائمة فحص التحصين: جمع كل العناصر معًا

يجمع البرنامج النصي الآمن في Bash والمناسب لبيئات الإنتاج كل تقنيات هذا الدرس ضمن دفاع متسق ومتعدد الطبقات. إليك قالبًا محصنًا بسيطًا لكنه متكامل:

  • set -euo pipefail — الخروج عند حدوث خطأ، ومعاملة المتغيرات غير المعيّنة كأخطاء، وتمرير حالات فشل الأنابيب.
  • تحقق عند الدخول — استخدم قائمة سماح لكل إدخال خارجي قبل أن يصل إلى أي أمر.
  • ضع كل شيء بين علامات اقتباس — "$var" و"$@" و"${array[@]}" — بلا استثناءات إلا عند الحاجة إلى التقسيم.
  • استخدم المصفوفات لإنشاء الأوامر الديناميكية.
  • أضف -- قبل الوسائط عند تمرير أسماء الملفات أو السلاسل التي يحددها المستخدم.
  • لا تستخدم eval أبدًا مع البيانات غير الموثوقة؛ وفضّل ${!var} للبحث غير المباشر.
  • قيّد الأذونات — شغّل البرامج النصية بأقل امتيازات مطلوبة، وتجنب sudo داخل البرامج النصية التي تقبل إدخال المستخدم.
#!/usr/bin/env bash
# Hardened template — safe argument injection prevention
set -euo pipefail
IFS=$'\n\t'

#--- 1. Validate inputs at the boundary ---
SEARCH_DIR="${1:-}"
PATTERN="${2:-}"

[[ -z "$SEARCH_DIR" || -z "$PATTERN" ]] && { echo 'Usage: script <dir> <pattern>' >&2; exit 1; }
[[ ! "$SEARCH_DIR" =~ ^[A-Za-z0-9/_.-]+$ ]] && { echo 'ERROR: unsafe directory path' >&2; exit 1; }
[[ ! "$PATTERN" =~ ^[A-Za-z0-9._-]+$ ]]    && { echo 'ERROR: unsafe pattern'        >&2; exit 1; }

#--- 2. Build command with an array ---
cmd=(find -- "$SEARCH_DIR" -type f -name "$PATTERN")

#--- 3. Execute — no string interpolation, no eval ---
echo "Executing: ${cmd[*]}"
"${cmd[@]}"

اختبار المعرفة: الاقتباس ومنع الحقن

اختبر مدى فهمك للمفاهيم الأساسية في هذا الدرس.

مراجعة الدرس: منع حقن الأوامر والوسائط

لقد غطّيت مجموعة الأدوات الدفاعية المتكاملة للتعامل الآمن مع إدخالات Bash:

  • يُعدّ تقسيم الكلمات وتوسيع الأنماط العامة الآليتين الأساسيتين اللتين تحولان المتغيرات غير الآمنة إلى نواقل للحقن.
  • ضع كل متغير بين علامتي اقتباس مزدوجتين ("$var" و"$@" و"${arr[@]}") لكبح التهديدين معًا.
  • استخدم "$@" — ولا تستخدم $@ أو $* من دون اقتباس — عند تمرير الوسائط.
  • أضف -- قبل أسماء الملفات التي يحددها المستخدم لمنع حقن الخيارات.
  • استخدم قائمة سماح لكل إدخال خارجي مع حارس يعتمد على تعبير نمطي ([[ $v =~ ^pattern$ ]]) قبل وصوله إلى أي أمر.
  • أنشئ الأوامر الديناميكية باستخدام المصفوفات (cmd+=() → "${cmd[@]}")، وليس بدمج السلاسل النصية.
  • تخلّص من eval وbash -c "$input"؛ واستخدم ${!varname} للتوسعة غير المباشرة الآمنة.
  • ابدأ البرامج النصية دائمًا بـ set -euo pipefail وIFS=$'\n\t' لتوفير أساس محصّن.

تقلل هذه الممارسات، عند تطبيقها باستمرار منذ السطر الأول في كل برنامج نصي، سطح الهجوم في Bash إلى ما يقارب الصفر بالنسبة إلى ثغرات فئة الحقن.

الأسئلة الشائعة

هل درس «منع حقن الأوامر والوسائط» مجاني؟

نعم — نص درس «منع حقن الأوامر والوسائط» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة DevOps Bootcamp، انتقل إلى CoddyKit PRO. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.

ماذا ستتعلم في «منع حقن الأوامر والوسائط»؟

استخدم الاقتباس والتحقّق وتمرير المداخل غير الموثوقة كمصفوفات للقضاء على تقسيم الكلمات والحقن المعتمد على eval تتمرن على DevOps Bootcamp مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ DevOps Bootcamp؟

لا تُشترط خبرة سابقة. DevOps Bootcamp على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «منع حقن الأوامر والوسائط»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس DevOps Bootcamp هذا؟

نعم. كل درس في DevOps Bootcamp يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. منع حقن الأوامر والوسائط
  2. التعامل الآمن مع الأسرار ونظافة البيئة
  3. التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo
  4. التحليل الساكن والتدقيق باستخدام ShellCheck
← العودة إلى DevOps Bootcamp