آشنایی با مفهوم XSS و جلوگیری از آن در زبان برنامه‌نویسی PHP

آشنایی با مفهوم XSS و جلوگیری از آن در زبان برنامه‌نویسی PHP

Cross-Site Scripting یا به اختصار XSS یکی از مهم‌ترین انواع حملات سایبری است که هکرها به کمک آن خواهند توانست اقدام به نفوذ در یک وب اپلیکیشن کنند. به عبارتی، XSS یکی از حملات سایبری است که در آن اصطلاحاً Code Injection (تزریق کد) صورت می‌گیرد بدین شکل که با دور زدن اپلیکیشن، به راحتی از طریق فرم‌های اینترنتی و یا یک لینک مخرب، به سایت نفوذ پیدا می‌کنند. 

هر زبان سمت سرور سازوکار اختصاصی خود را برای مقابله با حملاتی از این دست دارا است که در ادامه تمرکز روی زبان PHP است که امیدواریم با مثال‌هایی که در این مقاله زده می‌شود، مفهوم حملات XSS به خوبی روشن گردد (چنانچه علاقمند به فراگیری گام به گام زبان برنامه‌نویسی PHP هستید، می‌توانید به دورهٔ آموزش PHP در سکان آکادمی مراجعه نمایید.) برای شروع، فرم زیر را در نظر می‌گیریم:

<form action="post.php" method="post">
    <input name="comment" type="text" />
    <input name="submit" type="submit" value="Submit" />
</form>

در کد فوق یک فرم ساده می‌بینیم که در آن فیلدی وجود دارد که با استفاده از آن کاربر می‌تواند نظرات خود را وارد نماید و دکمه‌ای هم وجود دارد که با کلیک بر روی آن، اطلاعات وارد شده در فیلد مربوط به comment برای فایلی تحت عنوان post.php ارسال خواهد شد که حاوی اسکریپتی است که این فرم را هَندل خواهد کرد:

تنها کاری که اسکریپت قرار گرفته در فایل post.php انجام می‌دهد این است که محتوای وارد شده داخل فیلد مربوط به نظرات را به نمایش در می‌آورد. برای این منظور، کدهای زیر را داخل این فایل می‌نویسیم:

echo $_POST["comment"];

همان‌طور که در کد فوق می‌بینیم، دستور echo را نوشته سپس با استفاده از آرایه‌ای از پیش تعریف شده در زبان برنامه‌نویسی پی‌اچ‌پی تحت عنوان POST_$، مقدار قرار گرفته در فیلد comment را گرفته و به نمایش در می‌آوریم. با در نظر گرفتن این شرایط، هکری پیدا می‌شود که کد زیر را وارد فیلد کامنت در این فرم می‌کند:

<script>
    alert("hacked")
</script>

پس از کلیک روی دکمهٔ سابمیت، با تصویر زیر مواجه خواهیم شد:

همان‌طور که در تصویر فوق مشخص است، یک پاپاپ حاوی عبارت hacked روی صفحهٔ مرورگر نمایش داده می‌شود. این مثال صرفاً یک کد سادهٔ جاوااسکریپتی بود که یک پاپاپ را به نمایش در می‌آورد و جمله‌ای را به کاربر نمایش می‌دهد اما مسلماً هکرها می‌توانند از همین طریق کدهای به مراتب مخرب‌تری را وارد وب اپلیکیشن ما سازند و امنیت آن را به مخاطره اندازند.

اگرچه هکرها خیلی راحت با یکسری تکنیک‌ها می‌توانند به سایت ما حمله کنند، اما این در حالی است که راه‌های مقابله با XSS نیز بسیار راحت می‌باشد. ما به عنوان دولوپر وب همواره باید یک قانون کلی را در طراحی وب اپلیکیشن‌ها مد نظر داشته باشیم و آن هم اینکه به هیچ‌وجه به ورودی‌هایی که از سمت کاربران و یا سایر منابع به سمت وب‌سایت ما می‌رسد اعتماد نکنیم. به عبارتی، یک قانون طلایی برای مقابله با حملات XSS بدین صورت است که تک‌تک داده‌های ورودی را تست کرده سپس اجازهٔ ورود به اپلیکیشن یا دیتابیس را بدهیم.

اعتبارسنجی داده‌ها 
برای جلوگیری از حملات XSS نیاز است تا با مفهومی تحت عنوان Data Validation آشنا شویم که به صورت تحت‌الفظی می‌توان آن را به «اعتبارسنجی داده‌ها» ترجمه کرد. اعتبارسنجی داده‌ها به فرایندی گفته می‌شود که در آن این اطمینان را حاصل می‌کنیم که اپلیکیشن ما بر اساس داده‌های درست، صحیح و قابل‌اعتماد کار می‌کند.

به طور مثال، اگر اسکریپت ما برای اجرای درست نیاز به یک عدد صحیح (Integer) دارد، پس هر نوع دادهٔ دیگری مثل اعداد اعشاری، استرینگ و ... هرگز نباید اجازهٔ ورود پیدا کنند مضاف بر اینکه هر داده‌ای که کاربران از طریق فرم، یوآرال و ... وارد وب سایت می‌کنند، باید پس از دریافت اعتبارسنجی شوند تا مطمئن شویم که نوع داده ورودی همانی است که نیاز داریم و در غیر این صورت، جلوی کاربر گرفته شود.

برای مثال، اگر در فرمی از کاربر بخواهید تا شماره تلفن خود را وارد کند، باید اعتبارسنجی بدین صورت انجام شود که کاربر فقط و فقط بتواند عدد صحیح وارد کند و جلوی هر گونه استرینگ که حاوی عدد باشد گرفته شود. علاوه بر این، باید تعداد کاراکترهای این عدد صحیح را هم شمارش کنید تا مطمئن شود به طور مثال برای تلفن همراه در کشور ایران کاربر یک شمارهٔ 11 رقمی را وارد نموده است. برای روشن‌تر شدن این مسئله، کد زیر را در نظر می‌گیریم:

if (preg_match('/^((1-)?d{3}-)d{3}-d{4}$/', $phone)) {
    echo $phone . " is valid format.";
}

این اسکریپت شماره تلفن ورودی از سمت کاربر را اعتبارسنجی می‌کند و اگر کاربر چیزی به غیر از اعداد، یکسری علائم خاص مثل – که برای مجزاسازی بخش‌های یک شماره تلفن وارد کند، این داده معتبر نخواهد بود.

تمیز کردن داده‌ها 
در ادامه نیاز است با مفهوم دیگری تحت عنوان Data Sanitization آشنا شویم که در کل می‌توان معادلی همچون «تمیز کردن داد‌ه‌ها» برای این اصطلاح در نظر گرفت. این اصطلاح به فرایندی اشاره می‌کند که در آن دولوپر به پالایش داده‌ها می‌پردازد تا مطمئن شود که داده‌های ورودی دارای هیچ‌گونه کدهای ضمیمه‌شدهٔ اضافی نیستند.

برای مثال، در یک فرم ثبت‌نام در فیلد مربوط به وارد کردن نام و نام‌خانوادگی، ما این انتظار را از کاربر داریم تا یک استرینگ ساده وارد کند اما اگر کاربری پیدا شد و خواست تا علاوه بر وارد کردن نام و نام‌خانوادگی خود یکسری تگ‌های HTML هم ضمیمه کند، باید این تگ‌های اضافی را از استرینگ مد نظر حذف کنیم. برای روشن شدن این مسئله، کدهای زیر را در نظر می‌گیریم:

<form action="post.php" method="post">
    <input name="first_name" type="text" />
    <input name="last_name" type="text" />
    <input name="submit" type="submit" value="Submit" />
</form>

همان‌طور که در بالا مشاهده می‌شود، فرم ساده‌ای داریم که حاوی دو فیلد است که یکی مخصوص وارد کردن نام و دیگری مخصوص وارد کردن نام‌خانوادگی است. پس از کلیک کردن روی دکمهٔ سابمیت هم داده‌های فرم برای فایلی تحت عنوان post.php ارسال می‌شوند:

همان‌طور که در تصویر فوق مشخص است، در فیلد اول نام را داخل تگ‌های <strong></strong> قرار داده‌ایم که مسئول Bold کردن محتوای داخلش می‌باشند و در ادامه با استفاده از متد خاصی در زبان PHP قصد داریم تا داده‌های فرم را گرفته اما در صورتی که هر گونه تگی به همراه داده‌ها برای اسکریپت PHP ارسال شده بود، آن‌ها را حذف نماییم. برای این منظور، فایل post.php را به صورت زیر تکمیل می‌کنیم:

$first_name = strip_tags($_POST["first_name"]);
$last_name = strip_tags($_POST["last_name"]);
echo $first_name;
echo $last_name;

همان‌طور که در کد فوق می‌بینیم، دو متغیر داریم تحت عناوین first_name$ و last_name$ و از طریق آرایهٔ POST_$ فیلدهایی با همین نام‌ها را به عنوان مقدار این دو متغیر در نظر گرفته‌ایم. در زبان برنامه‌نویسی PHP متدی داریم تحت عنوان ()strip_tags که وظیفهٔ تمیزسازی داده‌ها را بر عهده دارد (واژهٔ Strip به معنی «لُخت کردن» است.) کاری که این متد انجام می دهد این است که هر پارامتری که برایش در نظر گرفته شود را از هر گونه تگی تهی می‌سازد. در ادامه هم با استفاده از دستور echo قصد داریم تا مقادیر این دو متغیر را نمایش دهیم:


می‌بینیم که تگ‌های <strong></strong> حذف شده‌اند و فقط نام و نام‌خانوادگی نمایش داده شده است.

پالایش خروجی
علاوه بر موارد فوق، مفهوم دیگری وجود دارد تحت عنوان Data Escaping که منظور از این اصطلاح این است که در جهت محافظت از یکپارچکی اطلاعاتی که در معرض دید کاربران قرار می‌گیرد، باید داده‌های خروجی را پالایش کنیم که با این کار مطمئن خواهیم شد مرورگر مورد استفادهٔ کاربر داده‌ها را با هیچ‌گونه معنی و مفهوم خاصی به غیر از آنچه مد نظر ما است در معرض دیدش قرار نخواهد داد. برای روشن شدن این مسئله، فرم بالا را در نظر می‌گیریم اما یک تغییر کوچک در اسکریپت موجود در فایل post.php خواهیم داد:

$first_name = htmlspecialchars($_POST["first_name"]);
$last_name = strip_tags($_POST["last_name"]);
echo $first_name;
echo $last_name;

همان‌طور که در کد فوق مشاهده می‌شود، متد در نظر گرفته شده برای متغیر first_name$ چیزی است تحت عنوان ()htmlspecialchars و کاری که این متد انجام می‌دهد این است که اگر کاربر هر گونه تگی را وارد فیلد مربوطه کرد، این متد اثربخشی آن تگ را از بین می‌برد و فقط به همان شکلی که کاربر واردش کرده نمایش داده می‌شود:


می‌بینیم که واژهٔ Behzad نمایش داده شده در حالی که تگ‌های آغازین و پایانی <strong></strong> در دو طرف آن قرار دارند بدون آنکه این واژه را Bold کرده باشند. حال اگر متد ()htmlspecialchars را از داخل اسکریپت خود حذف کنیم، خروجی کدهای فوق به صورت زیر خواهد بود:

نتیجه‌گیری 
زمانی که سایتی را در دسترس کاربران قرار می‌دهید، این بدان معنا است که طیف گسترده‌ای از افراد را مخاطب خود قرار داده‌اید که در این میان ممکن است با مجرمین سایبری، هکرها و تخریب‌گران آنلاین هم مواجه شوید و از همین روی منطق حکم می‌کند که تا حد توان، اپلیکیشن‌ خود را از دست این گروه از مخاطبین محافظت نمایید (البته نیاز به توضیح است که در این مقاله فقط اشارهٔ کوچکی به مفهوم XSS شده و هکرها راه‌های به مراتب پیشرفته‌تری از موارد ذکر شده در این مقاله را برای تخریب سایت‌ها مورد استفاده قرار می‌دهند.)

برای این منظور، باید هر گونه دادهٔ ورودی از سمت کاربران را مورد سنجش و ارزیابی قرار داده و در صورتی که جنس داده‌های ورودی با آنچه مد نظر شما بود تفاوت داشت، جلوی کاربر گرفته شده و این موضوع به اطلاع وی برسد تا اصلاح‌اش کند. علاوه بر این، اگر ورودی کاربران از آزمون اعتبارسنجی داده‌ها سربلند بیرون نیامدند، باید جلوی ورود آن‌ها به اپلیکیشن و دیتابیس گرفته شود و در نهایت هم برای آنکه مطمئن شویم کاربران همان خروجی که مد نظر ما است را مشاهده خواهند کرد، باید نتیجهٔ اسکریپت خود را پالایش نموده تا اطمینان حاصل کنیم مرورگرها همان چیزی که مد نظر ما است را در معرض دید کاربران سایت‌مان قرار می‌دهند.

آنچه مسلم است این که اکثر فریمورک‌های حرفه‌ای زبان پی‌اچ‌پی همچون لاراول، سیمفونی و غیره به صورت پیش‌فرض داده‌های ورودی فرم‌ها را اعتبارسنجی کرده و بار قابل‌توجهی را از دوش دولوپر برمی‌دارند اما باز با این حال باید خیلی به فریمورک هم اعتماد نکرده و شخصاً اقدام به تست اسکریپت‌هایی که می‌نویسیم نماییم.

همچنین در پایان اگر علاقمند به مباحث امنیتی هستید، می‌توانید به پادکست مصاحبه با مهران طُرِیحی: متخصص امنیت اطلاعات در رادیو فول‌استک مراجعه نمایید.

شما چه راه‌کارهای دیگری برای جلوگیری از حملات XSS پیشنهاد می‌دهید؟ نظرات، دیدگاه‌ها و تجربیات خود را با ما و سایر کاربران سکان آکادمی به اشتراک بگذارید.

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس


online-support-icon