آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

آموزش اسکریپت نویسی

آموزش اسکریپت نویسی پوسته گنو-لینوکس

پرسش و پاسخ شماره ۹۲

پرسش و پاسخ شماره ۹۲

چگونه یک اسکریپت CGI بنویسم که پارامترها را بپذیرد؟

همواره شرایطی ماورای کنترل ما وجود دارد، که ما را به سمت انجام مواردی می‌راند، که اگر به عهده خودمان بود هرگز انجام آنها را انتخاب نمی‌کردیم. این مدخل FAQ یکی از آن موقعیت‌ها را تشریح می‌کند.

یک برنامه CGI می‌تواند با پارامترهایی که توسط مرورگر شبکه ارسال گردیده، فراخوانی بشود . دو روش(حداقل) برای فراخوانی برنامه CGI وجود دارد: شیوه "GET" و شیوه "POST" . در شیوه "GET" ، پارامترها در یک متغیر محیطی به نام ‎QUERY_STRING‎ برای برنامه CGI فراهم می‌شوند. پارامترها قالب تعاریف ‎KEY=VALUE‎ را می‌گیرند(یعنی ‎user=george‎)، با برخی کاراکترهایی که به هگزادسیمال کدگذاری شده‌اند، فاصله‌ها به عنوان علامت به‌اضافه کُد شده‌اند، و همه آنها با کاراکترهای & به یکدیگر متصل شده‌اند. در شیوه "POST" ، در عوض پارامترها در ورودی استاندارد فراهم می‌شوند.

البته اینک ما می‌دانیم که شما هرگز نمی‌خواهید یک اسکریپت CGI در Bash بنویسید. بنابراین برای اهداف مورد نظر این مدخل، ما فرض خواهیم نمود که تروریستها همسر و فرزندان شما را دزدیده‌اند و اگر شما تقاضای آنان را درنوشتن چنین اسکریپتی اجابت نکنید، آنها را شکنجه می‌کنند، ضرب و جرح می‌سازند، و می‌کشند، «یا وخیم‌تر».

(موقعیت «یا وخیم‌تر» شاید به وضوح چیزی مشابه مجبور کردن شما به استفاده از نرم‌افزارهای مایکروسافت باشد.)

بنابراین، برای یک متغیر معین ‎QUERY_STRING‎، شاید بخواهیم کلیدها(متغیرها) و مقادیر آنها را استخراج کنیم، به طوری که بتوانیم آنها را در اسکریپت به کار ببریم.

روش سریع، آسان، و خطرناک برای پردازش QUERY_STRING، تبدیل &ها به ;ها و سپس استفاده از فرمان eval برای انجام تخصیص آنها می‌باشد. به هر حال استفاده از eval به شدت دلسرد کننده است. این است که ما همیشه می‌گوییم، اگر راه دیگری برای انجام آن وجود دارد، از eval پرهیز نمایید.

روش خطرناک

#  cgi در خواندن رشته ورودی در
if [ "$QUERY_STRING" ]; then
  foo=$QUERY_STRING
else
  read foo
fi

# (‏می‌ماند برای  تمرین  خواننده)‎ ‎"&"‎ تبدیل مقداری رشته کُد شده و مواردی مانند‎

# روی رشته eval اجرای‎
eval $foo

# ‏را در یک فیلد فرم وب قرار ‎"/bin/rm -rf /"‎ کنار بنشینید و تماشاکنید، کاربر‎
# .نباشد می‌تواند به بخشی از سیستم‌فایل صدمه وارد کند root داده است، که حتی اگر‎
# .یک رشته خطرناک دیگر می‌ تواند بمب خوشه‌ای باشد

روش ایمن

به جای گفتن آنکه پوسته هر کُدِ فراهم شده توسط کاربر در پارامترها را اجرا کند، رویکرد بهتر استخراج هر زوج متغیر/مقدار، و تخصیص آنها در متغیرهای پوسته، به طور یک به یک، بدون اجرای آنها می‌باشد. این کار یک تخصیص متغیر غیر مستقیم نیاز دارد، که به معنی استفاده از بعضی ترفندکاری‌های مختص پوسته می‌باشد. ما این ترفند را با ترکیب دستوری Bash می‌نویسیم، تبدیل آن به پوسته ksh یا Bourne به عنوان تمرین واگذار می‌شود.

# Bash

# cgi  خواندن رشته ورودی در
if [ "$QUERY_STRING" ]; then
  foo=$QUERY_STRING
else
  read -r foo
fi

#  می‌باشد name=Fred+Flintstone&city=Bedrock شامل موردی مشابه foo‎
#  متصل شده‌اند رفتار می‌کند ‎&‎ که با key=value این مانند یک لیست از عبارتهای‎
#  تکرار روی عناصر لیست و انجام عمل تخصیص  هریک 

IFS='&'; set -f
for i in $foo; do
    declare "$i"
done
unset IFS

# یک متغیر پوسته با همان نام  خواهد شد CGI اکنون هر پارامتر‎
# .بهتر است شما بدانید نامها کدام هستند، چون آنها را پیگردی نمی‌کنیم‎
#  است. فاصله به عنوان + کُد شده است "urlencoded" هر متغیر  بازهم ‎
# هگزادسیمال است ‎xx‎ کُد شده‌اند که ‎%xx‎‎‎ اقلام مختلف به عنوان‎

# را به کار ببریم ‎"name"‎ فرض کنید می‌خواهیم پارامتری به نام‎

# .اول رمزگشایی فاصله‌ها‎

name=${name//+/ }

# ما ترفند دیگری برای انجام آن به کار می‌بریم ‎%xx‎  اکنون رمز گشایی کاراکترهای‎
# ‎تعویض می‌کنیم \x‎ را با  ‎%‎ اول تمام علائم ‎
# ‎ها می‌شود\xxx‎ را به کار می‌بریم برای آنکه موجب ارزیابی تمام ‎echo -e‎ دوم، دستور

name=${name//\%/\\x}
name=$(echo -e "$name")

# این کار را قبل از تکرار و تخصیص حلقه انجام ندادیم چون اگر این کار را می‌کردیم‎
# کُد شده (یا هر کاراکتر بدخواهانه)‏ است ‎&‎ آنوقت یک پارامتر که شامل یک کاراکتر‎
# .موجب اندوه بسیار خواهد شد. ما باید این کار را در اینجا انجام بدهیم ‎

# انجام بدهید ‎"name"‎ حالا هر کاری مایل هستید با

در حالیکه شاید این روش قدری کمتر واضح باشد، از مشکل امنیتی بزرگی اجتناب می‌کند که eval دارای آن است: اجرای هر فرمان دلخواهی که ممکن بود کاربر، علاقمند به ورود آن از طریق یک فُرم وِب باشد. به طور واضح این یک بهبود است.

در این نگارش هنوز کاستی‌هایی وجود دارد. برای مثال، ما برای تضمین معتبر یا ایمن بودن نام متغیر پوسته، هیچگونه اعتبارسنجی روی طرف چپ(نام متغیر) در هر زوج ‎key=value‎ انجام نمی‌دهیم. اگر کاربر یک ‎PATH=‎ را در یک پارامتر استعلام وارد کند چه؟

آرایه‌های انجمنی

حتی یک رویکرد بهتر، می‌تواند قرار دادن زوج‌های کلید\متغیر داخل یک آرایه انجمنی باشد. آرایه‌های انجمنی در ksh93 و ‎bash 4.0‎ معتبر هستند، اما در POSIX یا پوسته‌های Bourne خیر. آنهابرای نگهداری زوج‌های ‎key/value‎ طراحی شده‌اند که در آن کلیدها می‌توانند رشته‌های اختیاری باشند، بنابراین به نظر می‌رسد برای این کار مناسب هستند.

# Bash 4+

#  cgi خواندن رشته ورودی‎
if [ "$QUERY_STRING" ]; then
  foo=$QUERY_STRING
else
  read -r foo
fi

# .برقراری آرایه انجمنی برای نگهداری پارامترهای پرس وجو‎
declare -A q

# ‎key=value+%41%42%43‎ تکرار روی عناصر‎
# .جدا سازی کلید و کمیت، و انجام رمزگشایی کمیت‎
IFS='&'; set -f
for i in $foo; do
    IFS='=' read key value <<< "$i"

    # ‎هاbackslash‎ حذف  ‎--‎ اول پاک سازی :‎ مراحل رمزگشایی ‎
    # .دوم، علامت‌های بعلاوه تبدیل به فاصله می‌شوند‎
    # .می‌شوند ‎\x‎ سوم، علائم درصدتبدیل به ‎
    # .را موجب گردد printf چیزی باقی نمی‌گذارد که بتواند به طور غیر منتظره یک بسط ‎‎
    # .ها مال ما هستند و هیچ علامت درصد باقی نماندهbackslashe تمام ‎

    value=${value//\\/}
    value=${value//+/ }
    value=${value//\%/\\x}
    printf -v final -- "$value"
    q["$key"]="$final"
done
unset IFS

# .به کار ببریم q اکنون می‌توانیم پارامترها را از آرایه انجمنی با نام ‎
#  ‎${!q[*]}‎ اگر لیستی از کلیدها را لازم داشته باشیم، این است‎

در اینجا مرحله پاکسازی بینهایت مهم است. بدون آن اقدام احتیاطی، فرمان printf می‌تواند به واسطه یک رشته قالب‌بندی مهاجم، آسیب پذیر باشد. گزینه ‎printf -v varname‎ در هر نگارش از bash که از آرایه‌های انجمنی پشتبانی کند، معتبر است، بنابراین می‌توانیم از آن در اینجا استفاده کنیم. خیلی بیشتر از فراخوانی پوسته فرعی مؤثر است. همچنین از مشکلات بالقوه‌ ‎echo -e‎ در صورتی که اتفاقاً value موردی مانند ‎-n‎ باشد، اجتناب نموده‌ایم.

به طور تکنیکی، خصوصیات CGI چندین نمونه از یک کلید را در یک استعلام منفرد اجازه می‌دهد. برای مثال، ‎group=managers&member=Alice&member=Charlie‎ یک رشته کاملاً مشروع پرس وجو می‌باشد. هیچ یک از رویکردها در این صفحه این حالت را مدیریت نمی‌کند(حداقل نه به طریقی که ما احتمالاً آنرا روش صحیح در نظر بگیریم). خوشبختانه، غالباً اینطور نیست که شما بخواهید یک اسکریپت CGI مانند این بنویسید، و در هر حال، شما مجبور نیستید برای انجام این وظیفه از bash استفاده کنید.


CategoryShell

پرسش و پاسخ 92 (آخرین ویرایش ‎2010-04-16 23:58:26‎ توسط GreyCat)