Defensive Programming (برنامهنویسی تدافعی) یک سَبک برنامهنویسی با هدف پیشبینی حفرههای امنیتی و نقاط ضعف احتمالی است که هدف نهایی این رویکرد توسعهٔ نرمافزار جلوگیری از بروز مشکلات احتمالی قبل از اتفاق افتادن آنها است. به طور کلی، ایدهٔ Defensive Programming این است که «غیرممکن، ممکن است گاهی ممکن گردد» و مسلماً زمانی که پای امنیت به میان میآید، برنامهنویسی بدین شکل خوش خواهد درخشید که به منظور پیادهسازی این رویکرد در پروژههای نرمافزاری، باید همواره مواردی که در ادامه معرفی میکنیم را مد نظر داشته باشیم.
هیچگاه به دادههای کاربران اعتماد نکنید
به عنوان یک قانون کلی، هیچوقت به Input یا بهتر بگوییم دادههایی که توسط کاربران وارد سیستم میشوند اعتماد نکنید و همه چیز را قبل از ثبت در دیتابیس چک کنید. به طور مثال، اگر کاربر قرار است در یک فرم عکسی را آپلود کند، هیچگاه پسوند تصاویر که کاربر نمیتواند استفاده کند (Blacklist) را چک نکنید بلکه برعکس، یک لیست از پسوندهایی که مورد تأییدتان هستند (Whitelist) تهیه کرده و کلیهٔ ورودیها را با آن بسنجید.
در پاسخ به این پرسش که چرا به جای Blacklist باید از Whitelist استفاده کنیم، باید گفت که چنانچه از لیست سیاه استفاده کنیم، همواره این احتمال وجود دارد که پسوندی از قلم بیافتد اما اگر از یک لیست سفید استفاده نماییم، مثلاً صرفاً پسوندهای jpg و png، خیالمان راحت خواهد بود که سیستم راه نفوذی ندارد.
کوئریهای درست برای دیتابیس ارسال نمایید
SQL Injection همواره یکی از آسیبپذیریهای رایج در سراسر دنیا است (برای آشنایی با این مفهوم، به مقالهٔ آشنایی با مفهوم SQL Injection در زبان PHP مراجعه نمایید.) با استفاده از ابزارها و لایبرریهای ایمن برای ارتباط با دیتابیس، تا حد قابلتوجهی میتوان این تضمین را ایجاد کرد که کوئریهای مخرب وارد دیتابیس نخواهد شد. به طور مثال، در زبان PHP میتوان از لایبرری PDO استفاده کرد که برای آشنایی بیشتر با این لایبرری میتوانید به مقالهٔ ارتباط با دیتابیس در PHP از طریق لایبرری PDO مراجعه نمایید.
هیچگاه به فریمورکها اعتماد نکنید
درست است که بسیاری فریمورکها و لایبرریهای اپنسورس هستند که در سراسر دنیا دولوپرهای بسیاری از آنها استفاده میکنند، تستشده هستند و هیچ راه نفوذی ندارند اما هیچگاه مبنا را این قرار ندهید که فریمورک یا لایبرری مورد استفادهٔ شما ٪۱۰۰ ایمن است.
هرگز به کدهای دیگر دولوپرها اعتماد نکنید
در واقع همانطور که رانندگی میکنیم همواره باید این نکته را مد نظر داشته باشیم که علیرغم دقت خودمان در رانندگی این احتمال وجود دارد که دیگر رانندگان بیدقتی کرده و خطراتی را هم برای خودشان و هم برای ما به وجود آورند، در کدنویسی هم چنین قانونی صادق است بدین صورت که هرگز به کدهای دیگر دولوپرها اعتماد نداشته باشید و همه چیز را به دقت تست کنید چرا که ممکن است دولوپری که روی سورسکد مد نظر کد زده، فرد بیدقتی بوده باشد.
دستورات شرطی را به صورت کامل بنویسید
این مورد را با ذکر یک مثال توضیح خواهیم داد. فرض کنیم متغیری داریم تحت عنوان var$
که میتواند مقداری همچون 1 یا 2 داشته باشد و بسته به این مقدار، یکی از دستورات شرطی زیر اجرا خواهند شد:
$var = 1;
if ($var == 1) {
echo 'It`s One';
} elseif ($var == 2) {
echo 'It`s Two';
}
چنانچه مقدار متغیر var$
برابر با 1 باشد، دستور داخل if
اجرا خواهد شد و چنانچه این مقدار برابر با 2 باشد، دستور داخل elseif
اجرا میگردد اما اگر این مقدار چیزی به غیر از مقادیر فوقالذکر باشد هیچ حالت سومی برای هَندل کردن این موضوع در نظر گرفته نشده است!
آنچه برنامهنویسی Defensive به ما حکم میکند این است که حتی اگر هم مقدار متغیر var$
توسط خود دولوپر پُر میشود و ٪۱۰۰ یکی از مقادیر 1 یا 2 خواهد بود، باز هم احتمال دهید که ممکن است روزی بنا به شرایط خاصی این مقدار تغییر یابد و نیاز است کدی بنویسید که این موضوع در آن دیده شده باشد به طوری که خواهیم داشت:
$var = 'none';
if ($var == 1) {
echo 'It`s One';
} elseif ($var == 2) {
echo 'It`s Two';
} else {
echo 'None of Them';
}
میبینیم که اگر مقدار این متغیر اِسترینگی همچون none شود، به سادگی وارد دستور else
شده و برنامه به خوبی مدیریت خواهد شد و از اجرای سایر اسکریپتها جلوگیری به عمل خواهد آمد.
گزینهٔ default برای دستورات سوئیچ را هرگز از یاد نبرید
سوئیچ هم نوعی دستور شرطی است. برای روشنتر شدن آسیبپذیریهای مرتبط با آن، اسکریپت زیر را در نظر میگیریم:
$var = 1;
switch ($var) {
case 1: echo 'It`s One';
break;
case 2: echo 'It`s Two';
break;
}
در تفسیر اسکریپت فوق باید بگوییم که اگر مقدار متغیر var$
برابر با 1 باشد، کیس اول اجرا خواهد شد و اگر این مقدار برابر با 2 باشد، وارد کیس دوم خواهیم شد اما اگر این مقدار برابر با 3 باشد، هیچ شرطی برای این منظور در نظر گرفته نشده و اینجا است که نیاز داریم همواره به خاطر داشته باشیم در حین استفاده از دستور سوئیچ باید کیس پیشفرض default
را هم در نظر بگیریم:
$var = 3;
switch ($var) {
case 1: echo 'It`s One';
break;
case 2: echo 'It`s Two';
break;
default: echo 'It`s None of Them';
break;
}
به عبارت دیگر، اگر هیچ کدام از شروط فوق برآورده نشوند، دستورات داخل default
اجرا خواهند شد به طوری که داخل این دستور میتوان یک اِکسپشن نوشت تا کسی که اپلیکیشن را مدیریت میکند متوجه موضوع گردد (به طور کل، default
در شرایطی خاص از اجرای سایر اسکریپتها جلوگیری به عمل خواهد آورد و این دقیقاً همان چیزی است که نیاز داریم.)
نتیجهگیری
Defensive programming به طور خلاصه حاکی از آن است که اپلیکیشن باید رفتاری ثابت و قابلپیشبینی داشته باشد حتی در شرایطی که کاملاً قابلپیشبینی نیستند. برای مشخص شدن دلایل اهمیت نوشتن یک اپلیکیشن ایمن، در ادامه به چند مورد تکاندهندهای اشاره میکنیم که باگها منجر به از دست رفتن جان انسانها و یا ضررهای هنگفت شدهاند:
- وجود باگی در نرمافزار یک سیستم رادیولوژی در سال 1980، باعث مرگ ۵ بیمار شد.
- باگی در نرمافزار سیستم دفاع هوایی موشکی MIM-104 Patriot در سال 1991 باعث میشد که در هر 100 ساعت، یکسوم ثانیه ساعت این سیستم جلو میافتاد که در نهایت به خاطر اشتباه در هدفگیری، منجر به کشته شدن ۲۸ آمریکایی شد.
- سفینهٔ فضایی Ariane 5 به ارزش یک میلیون دلار متعلق به آژانس فضایی اروپا به خاطر وجود یک باگ در نرمافزارش در سال 1996، صرفاً ۴۰ ثانیه پس از بلند شدن منفجر شد.
به نظر میرسد که مثالهای فوقالذکر فجایعی که بیدقتی در کدنویسی میتواند به بار بیاورد را به خوبی روشن کرده باشند. حال نوبت به نظرات شما میرسد. آیا رویکرد کدنویسی Defensive را در حین انجام پروژههای نرمافزاری مد نظر دارید و آیا پیادهسازی این رویکرد بسته به زبانهای برنامهنویسی مختلف میتواند متفاوت باشد و در نهایت اینکه آیا این رویکرد ٪۱۰۰ تضمین میکند که در شرایط خاص، اپلیکیشن عملکردی قابلپیشبینی از خود نشان خواهد داد؟ نظرات، دیدگاهها و تجربیات خود را با سایر کاربران سکان آکادمی به اشتراک بگذارید.