آموزش اصلاح کدها و جلوگیری از بوجود آمدن رخنه و باگ امنیتی تزریق کد (XSS) یا همان Cross Site Scripting در زبانهای PHP و ASP و ASP.NET (یا همان ASPX).
مختصری درباره باگ XSS:
حمله تزریق اسکریپت از طریق وبگاه (Cross Site Scripting) که به صورت مخفف XSS نیز نامیده میشو، نوعی از حملات مبتنی بر وب است که در بسیاری از وبسایتها وجود دارد. متاسفانه خیلی از توسعه دهندگان وب این باگ را نادیده میگیرند یا اطلاع کافی درباره جلوگیری از آن ندارند درحالی که این رخنه امنیتی بسیار خطرناک است.
در باگ کراس سایت اسکریپتینگ، هکر یا حمله کننده با استفاده از متدهای مختلف، اسکریپت جاوا اسکریپت یا کد HTML دلخواه خود را به کاربر یا کاربران یک صفحه که در آن باگ XSS وجود دارد، تزریق میکند. سپس این کدها در سمت مرورگر کاربر اجرا شده و کاربر را در مقابل خطرات مختلف و بسیار خطرناک قرار میدهد.
برای اطلاع بیشتر درباره این باگ، نحوه کارکرد و خطرات ناشی از آن، پست “حمله تزریق کد (Cross Site Scripting) یا XSS چیست؟” را بخوانید.
نحوه جلوگیری از باگ XSS
پیش نیاز این قسمت، درک نحوه کارکرد باگ XSS است که در پست “حمله تزریق کد (Cross Site Scripting) یا XSS چیست؟” توضیح داده شده است.
در این بخش همه روشهای مقابله با XSS به دلیل پیچیدگیهای مختلف آن نوشته نشده است. به بخش آخر این پست (“در نهایت …”) دقت کنید.
حال که با نحوه کار این رخنه امنیتی آشنا شدیم، سراغ پچ و اصلاح کردن کدهایمان میرویم. با توجه به این که وجود این باگ ناشی از فیلتر نکردن ورودی/خروجی ها در سمت سرور است و زبان سرور ممکن است متفاوت باشد، ما نحوه جلوگیری از باگ را در دو زبان رایج یعنی PHP و ASPX (یا همان ASP.NET) توضیح خواهیم داد.
به ورودی/خروجیها اعتماد نکنید.
به هیچ وجه به دادههای ورودی کاربران اعتماد نکید. حتی درصورتی که دادهها را فیلتر کنید، به هیچ وجه آنها را در قسمتهای زیر قرار ندهید:
توضیحات | نمونه کد |
در داخل تگ Script | <script>دادههای غیرقابل اطمینان</script> |
در داخل کامنتهای HTML | <!– دادههای غیرقابل اطمینان –> |
در نام خاصیتها (Attribute name) | <tagname دادههای غیرقابل اطمینان=”test”> |
در نام تگها (Tag name) | <دادههای غیرقابل اطمینان id=”test”> |
در داخل تگ Style و کدهای CSS | <style>دادههای غیرقابل اطمینان</style> |
و در هیچ جای دیگر! برای مثال: | <p>دادههای غیرقابل اطمینان</p> |
باور کنید گذاشتن دادهها در این قسمتها اصلاً لازم نیست. بجای آن میتوانید از اعمال مقایساتی استفاده کرده سپس دادههای ثابت (نه ورودی کاربر) را قرار دهید.
هر چیزی که چاپ میکنید را فیلتر یا اسکیپ کنید.
قبل از نشان دادن یا چاپ کردن مقادیر ورودی کاربر (یا مقادیری که کاربر قبلاً ثبت کرده است)، حتماً آنها را Escape کنید. اسکیپ کردن دادههای HTML این امکان را فراهم میکند تا چیزی که قرار است چاپ شود، توسط مرورگر به عنوان کد HTML شناخته نشده و فقط به عنوان یک متن عادی نشان داده شود.
در این گونه موارد قبل از هر چیزی، کاراکتر اینکدینگ (Character Encoding) خود را مشخص کنید. معمولاً UTF-8 یا ISO-8859-1. این مورد بسیار مهم است زیرا بدون مشخص کردن اینکدینگ ممکن است اسکریپت ما مثلاً به XSS هایی تحت اینکدینگ UTF-7 آسیب پذیر باشند.
در ادامه، خروجیای که قرار است چاپ شود را فیلتر یا اسکیپ کنید.
نکته: حتی با انتخاب کاراکتر ست و فیلتر و اسکیپ کردن خروجی با استفاده از توابع زیر، به هیچ وجه ورودیهایی با اسکریپت خالص را نشان ندهید. در برخی از تگها و خواص آنها، امکان اجرا شدن آن اسکریپت وجود خواهد داشت. در این گونه موارد بهتر است از encodeForJavaScript کتابخانه ESAPI استفاده کنید.
در زبان PHP:
ابتدا کاراکتر ست ای را که با آن کار میکنیم و قرار است مرورگر کاربر ما از آن استفاده کند را مشخص میکنیم:
header('Content-Type: text/plain; charset=utf-8');
حال با استفاده از توابع htmlspecialchars یا htmlentities خروجی خود را فیلتر کنید:
$safe_output = htmlentities($untrusted_output, ENT_QUOTES | ENT_HTML401, ‘UTF-8’);
echo “<div>$safe_output</div>”;
در زبان ASPX:
در ASP.NET یا ASPX هم ابتدا کاراکتر ست را مشخص میکنیم:
<%@ Page RequestEncoding=”utf-8″ ResponseEncoding=”utf-8″ %>
سپس با استفاده از تابع خروجی را اسکیپ میکنیم:
safe_output = HttpUtility.HtmlEncode(untrusted_output);
Response.Write(safe_output);
مقادیر جاوا اسکریپت را هم فیلتر و اسکیپ کنید.
همانطور که گفته شد، فیلتر یا اسکیپ کردن جاوا اسکریپت با HTML متفاوت است و نباید از توابع بالا برای فیلتر کردن کدهای جاوا اسکریپت استفاده نمود. برای اسکیپ کردن مقادیر جاوا اسکریپت بهترین کار استفاده از تابع encodeForJavaScript در کتابخانه ESAPI استفاده کنید. با این حال، میتوانیم روشهای زیر را نیز به کار گیریم.
مثل قانون دوم، در همه جا ما کاراکتر ست را برای مرورگر کاربر تعریف میکنیم. برای کوتاه کردن کدها، این قسمت را دیگر نخواهیم نوشت.
در زبان PHP:
مثلاً اگر لازم است جاوا اسکریپتی را که حاوی مقادیر غیرقابل اعتماد است را چاپ کنید، بهتر است از تابع json_encode که به صورت UTF-8 اینکد میکند، استفاده کنید:
<script type=”text/javascript”>
var unsafe_output = ‘<?= addslashes($untrusted_output) ?>’; // این تابع آسیب پذیر است
var safe_output = <?= json_encode($untrusted_output) ?>; // این تابع امن است
</script>
در زبان ASPX:
<script type=”text/javascript”>
var safe_output = <%=HttpUtility.JavaScriptStringEncode(untrusted_output) %>; // این تابع امن است
</script>
URLهای مشکوک را فیلتر کنید.
اگر در قسمتی از کدهای HTML خود هنگام ارسال به کاربران، آدرسی وجود داشت که توسط کاربری مشخص میشود، حتماً قبل از نشان دادن آن، فیلترش کنید.
در زبان PHP:
$safe_URL = htmlspecialchars(urlencode($untrusted_URL));
echo ‘<a href=”http://example.com/place/”‘ . $safe_URL . ‘”>Click On Me</a>’;
در زبان ASPX:
safe_URL = HttpUtility.UrlEncode(untrusted_URL);
Response.Write(safe_URL);
امنیت اضافی
همانطور که میبینید، جلوگیری از XSS در سناریوهای متفاوت باید با روشها و فیلترهای مختلفی انجام گیرد. هرچقدر هم که وبسایت یا سرویس خود را امن کنیم، باز هم خطر XSS وجود خواهد داشت. بنابراین باید از متدهای زیر نیز برای امن کردن بیشتر استفاده کنیم:
از کتابخانههای مخصوص XSS استفاده کنید.
فیلتر و اسکیپ کردن خروجیها بصورت دستی و تشخیص رخنهها کار بسیار سختی است. از این رو کتابخانههای بسیار امنی مانند HTML Purifier و HtmlSanitizer بوجود آمده اند. شدیداً توصیه میشود که از این کتابخانهها برای اسکیپ کردن خروجیهای خود استفاده کنید.
HttpOnly کوکیها را فعال کنید.
با فعال کردن فلگ HttpOnly کوکیها فقط با درخواستهای HTTP ارسال خواهند شد و در این مسیر، حمله کننده با باگ XSS هم نخواهد توانست به کوکیهایی که با HttpOnly علامت گذاری شده اند دسترسی داشت.
برای اطلاعات بیشتر پست “کوکی (Cookie) چیست؟” را بخوانید.
همیشه کاراکتر ست را مشخص کنید.
چه با PHP کار کنید و چه با ASPX و چه زبانهای دیگر، همیشه به یاد داشته باشید که مشخص کردن کاراکتر ست (معمولاً UTF-8) برای صفحات HTML هم در هدر پاسخ درخواست و هم در کدهای HTML بسیار مهم است.
از Content Security Policy استفاده کنید.
این هدر که در پاسخ درخواست مرورگر ارسال میشود، به مرورگر لیست سفیدی (White List) از آدرسهایی که مرورگر اجازه دانلود منابعی مانند کدهای جاوا اسکریپت، استایل شیتها (CSS)، تصاویر و … را دارد، میدهد. بدین ترتیب جلوی بسیاری از XSS های مبتنی بر منابع خارجی گرفته میشود.
از X-XSS-Protection استفاده کنید.
این یک هدر است که در پاسخ به درخواست مرورگر ارسال میشود. مرورگرهای مدرن قابلیتهای متعددی در مقابله با حملات XSS دارند که معمولاً به صورت پیشفرض فعال است. کار از محکم کاری عیب نمیکند، پس این هدر را به صورت فعال به مرورگر کاربر بفرستید.
اگر از iframe استفاده میکنید، آن را محدود کنید.
برخی از مرورگرها مانند اینترنت اکسپلورر با دادن مقدار restricted به خاصیت security در تگ iframe، صفحه آی-فرم را به صورت موقت در لیست صفحات منع شده قرار داده و به صورت پیشفرض، اسکریپتی از آن صفحه را اجرا نمیکند. این آپشن میتواند تا حدودی امنیت XSS از طریق iframe ها را تامین کند.
در نهایت …
امنیت به هیچ وجه ۱۰۰% نیست. مانند سایر باگها، ممکن است XSS نیز در پی بی توجهی برنامه نویس به مسائل امنیتی، در قسمتهایی از برنامه تحت وب وجود داشته باشد که پیدا کردن آن حتی به فکر خود برنامه نویس هم ممکن است خطور نکند.
علاوه بر این، چیزهایی که در این پست گفته شده است فقط و فقط قسمتی از نحوه تامین امنیت سایت در مقابل XSS است و به هیچ وجه نمیتواند یک منبع کاملاً امن به شمار برود. برای اطلاع بیشتر از نحوه جلوگیری از باگ XSS و استراتژیهای مختلف در سناریوهای متفاوت، به چیت شیت OWASP XSS Prevention مراجعه کنید.
منابع این پست:
صفحات زیر منابع اصلی این پست هستند با این حال تغییرات بسیاری روی آنها اعمال شده است:
سلام !
قالب سایت رو دوست دارم، شیک و ساده
مرسی بابت مطالب… 🙂
ایشاا… سایت بهتر و بهتر بشه…
سلام، ممنون! چطوری آقا سپهر؟ 🙂 چه خبرا داداش؟
مرسی، سلامت باشی…
مشغولیمم 🙂
زنده باشی داداش 😉