کنترل ورودی کاربر، چرا و چگونه؟
در این مطلب، قصد داریم با شما برنامهنویس عزیز، که وظیفهی راهاندازی و توسعهی سامانهها به ویژه سامانههای تحت وب را برعهده دارید، نکاتی را در میان بگذاریم. گرچه شاید برخی از این نکات، بدیهی به نظر بیایند اما بیتوجهی و عدم رعایت آنها، میتواند باعث ایجاد آسیبپذیریهایی شود و دروازهای را برای ورود و حملات هکرها مهیا سازد. ما در این مطلب به نکاتی برای کاهش خطراتی که در کمین سامانهها هستند، میپردازیم، دربارهی نحوهی رفتار سامانه با دادههایی که کاربر در فرمها وارد کرده است، صحبت خواهیم کرد و سپس به نحوهی امنسازی انتقال این دادهها برای عدم ایجاد اختلال و نفوذ در سامانه خواهیم پرداخت. با ما همراه باشید.
بگذارید با چند سوال آغاز کنیم:
• آیا میتوان به همهی دادههای ورودی کاربران نگاه یکسان داشت؟
• آیا میتوان به یکپارچگی درخواست دریافتی از سمت مرورگر کاربر اعتماد داشت؟
• آیا میتوان اطمینان داشت که ارتباط بین مرورگر کاربر با برنامهی ما غیرقابل دستکاری است؟
پاسخ سوالات بالا منفی است. به طور خلاصه میتوان گفت که شما در هیچ شرایطی نمیتوانید مطمئن باشید که ارتباط کاربر با سامانه کاملا ایمن است. به همین دلیل لازم است که برنامهنویس سامانه تمام ورودیها و خروجیهای سامانه را بررسی کند و اقداماتی را به کار ببندد تا کاربر تنها بتواند آن مواردی را که انتظار میروند و موردنیازند، در ورودیها وارد و در خروجیها دریافت کند. و اینگونه و با این اقدامات میشود ورودی کاربر را تا حدودی کنترل کرد و میزان امنیت موجود در ارتباط بین کاربر و سامانه را بالاتر برد.
"فرم" و "ورودی کاربر" به چه معنی هستند؟
در طول این مطلب با عباراتی نظیر ورودی کاربر و فرم مواجه خواهیم شد که برای روشنترشدن مقصود، در همین ابتدا تعریفی کوتاه از این مفاهیم ارائه میکنیم: به هر مورد فیلد ورودی داده یا مکانی که بتوان با استفاده از لوازم ورودیای مانند صفحهکلید در آن مقداری را وارد کرد، فرم میگوییم راحتتر بگوییم: فرم همان جایگاه دریافت ورودی کاربر است. فرمها به طور خاص به تگ <form> و <input> در HTML نیز اطلاق میشوند. مانند: یک فرم ثبتنام و یا ورود به سامانه. و ورودی کاربر، همان اطلاعات و مقادیری هستند که کاربر در محل فرمها وارد میکند.
آیا لازم است ورودی کاربر را کنترل کرد؟
اگر بخواهیم در ابتدا به مفهوم اعتبارسنجی بپردازیم، باید بگوییم که اعتبارسنجی ورودی کاربر به این مفهوم است که در فرم ورود برای ورودی کاربر محدودیتهایی را ایجاد کنیم که باعث شود دادههای موردنیاز را در قالبی که در سمت سرور انتظار آن را داریم، دریافت کنیم و در نتیجهی این محدودیتها، از ورود دادههای ناایمن مانند اسکریپتهای مخرب و دستورات SQL به فرآیندهای سمت سرور جلوگیری کنیم. با این اقدام ما تنها ورودیهای مطابق با الگو و چارچوب تعیینشدهی کاربران را دریافت خواهیم کرد.
اگر ورودی کاربر را کنترل نکنیم...
برای روشنتر شدن موضوع سراغ یک مثال میرویم: یکی از خطراتی که فرمهای کنترلنشده میتوانند برای سامانه به وجود بیاورند، عدم رفع کارکترهای ویژه در ورودیهاست. شاخصترین کارکتر ویژه، کارکتر کوتیشن ' است که در صورت کنترل نشدن میتواند باعث ایجاد خطرهای گوناگونی برای سامانه شود که بروز آسیبپذیری XSS یکی از مهمترین آنهاست. فرض کنید که در تگ script کدی به شکل زیر نوشته شده باشد:
همانطور که میبینید نام شخص شامل یک کوتیشن است و به این دلیل که کوتیشن در ساختار دستور نیز وجود دارد، سامانه دچار اشتباه در تمییز کوتیشن موجود در نام و کوتیشن موجود در دستور میشود و اینگونه پس از کوتیشن دوم امکان تزریق دستور دیگری برای مهاجم فراهم میشود. حالا اگر مهاجم از این نقص بهرهجویی و کد زیر را وارد کند:
کاربر به طور خودکار به آدرس تزریقشده هدایت میشود. این همان سایت مهاجم است که انتظار ورود کاربر را میکشید و به محض ورود کاربر تمام اطلاعات اعتبارسنجی کاربر، از جمله کوکی و موارد دیگر، قابلسرقت خواهند بود. در ادامه نیز هکر میتواند با استفاده از همان اطلاعات هویت خود را جعل و با هویت همان کاربر وارد سامانه شود. اگر آن کاربر از سطح مدیریت بالاتری برخوردار باشد، قطعا سطح دسترسی بالاتری هم پس از نفوذ در اختیار مهاجم قرار میگیرد.
اگر ورودی کاربر را کنترل بکنیم...
برای جلوگیری از این موارد، کد بالا میتواند به شکل زیر کنترل شود.
اینگونه داستان طور دیگری رقم میخورد؛ اینگونه یکی از استراتژیهای حملهی احتمالی مهاجمان، خنثی و سامانه اندکی امنتر خواهد شد.
این کار به صورت دستی امکانپذیر است اما با استفاده از کتابخانهها یا توابع موجود در زبانهای برنامهنویسی نیز قابلانجام است. که در ادامهی مطلب مفصلتر به هر یک خواهیم پرداخت.
اعتبارسنجی چگونه باعث افزایش امنیت میشود؟
هکرها میتوانند با ارسال کدهای مخرب از طریق فرمهای ورود برای ورودی کاربر در صفحات سامانه، آسیبپذیریهای مختلفی را که احتمالا در سامانه وجود دارند، کشف و اکسپلویت کنند. این کدهای مخرب میتوانند از نوع اسکریپت XSS، پرسشهای SQL یا کدهای مخرب دیگری باشند که عدم کنترل آنها، زمینهی نفوذ هکرها به سامانه را فراهم میکند و مقدمهای بر اقدامات خرابکارانه بعدی هکرها خواهند بود. از همین بابت، نکتهی مهمی که باید حتما به آن توجه داشت این است که چون استفاده از معماری RESTFul به شدت افزایش پیدا کرده است، بهتر است اعتبارسنجی یا Validation ورودی کاربر در هر دو سمت، کاربر و سرور، انجام شود؛ این کار اگر فقط در سمت کاربر صورت بگیرد، خوب است اما کافی نخواهد بود و میزان خطر متوجه سامانه را چندان کاهش نمیدهد. چراکه تنها راه مهاجم برای ارتباط با سرور فرمهای صفحات سامانه نیستند، بلکه امکان تعامل مستقیم با وبسرویس از طریق آدرس Endpoint نیز برای مهاجم مهیاست. هکر میتواند در این حالت به طور مستقیم با سرور در ارتباط باشد. اینگونه با اعتبارسنجیهای سمت کاربر ،که فقط در مقادیر فرمها انجام میشوند، مواجه نخواهد شد و به راحتی این اعتبارسنجیها را دور خواهد زد. بنابراین، نیاز است که اعتبارسنجی ورودی کاربر در درجهی اول سمت سرور انجام شود. البته اگر سمت کاربر نیز اعتبارسنجی صورت بگیرد، یک لایهی امنیتی مضاعف اضافه میشود و بار روی دوش سرور را کمتر میکند.
انواع اعتبارسنجی
اعتبارسنجی در فرمها در جهت کنترل ورودی کاربر، به طور کلی به دو حالت قابلاعمال است: تعریف الگوی مجاز و یا تعریف الگوی غیرمجاز. اگر اعتبارسنجیها به صورتی باشند که کاربر توانایی ورود دادهها در فرم ورود را فقط در قالب مشخصی داشته باشد، این اعتبارسنجی از نوع ایجاد یک WhiteList خواهد بود که ما آن را اعتبارسنجی مثبت مینامیم.
حالت دوم به این صورت است که برای سامانه تنها یک لیست سیاه شامل موارد ممنوعه تعریف میشود، سامانه ورودی کاربر را از فیلتر این لیست سیاه عبور میدهد و در صورتیکه موارد ممنوعه در آن وجود نداشته باشند، آن را میپذیرد. در غیر این صورت آن را رد میکند. در این نوع اعتبارسنجی که کاربر در شکل ورود دادهها در فرمها آزاد است و از نوع ایجاد یک BlackList است را ما یک اعتبارسنجی منفی مینامیم.
اعتبارسنجی «مثبت»:
همانطور که گفته شد، اعتبارسنجی مثبت فقط اجازهی ورود داده طبق یک الگوی مشخص را به کاربر میدهد؛ یعنی کاربر نمیتواند به شکلی دیگر دادهها را وارد کند و ورودی کاربر باید حتما منطبق با الگوی خاصی باشد. به عنوان نمونه: اگر در یک فرم ثبتنام، فیلدهایی برای ورود مواردی نظیر پست الکترونیکی، شماره تلفن و غیره وجود داشته باشند، تنها در صورتی که دادهها مطابق الگوی مشخصشده برای ورودی کاربر در فیلد ورودی فرم وارد شوند، پذیرفته میشوند و ورودیهای مغایر با الگوی فوق، ثبت نمیشوند و با خطا مواجه خواهند شد. سادهترین حالت به این شکل است که برای فیلد پست الکترونیکی الگویی مشخص در نظر گرفته شده است که ورودی کاربر باید حتما شامل @ باشد و اگر ورودی کاربر بر خلاف آن الگو و بدون کاراکتر @ باشد، پذیرفته نخواهد شد. همچنین در نمونهای دیگر فیلد مربوط به شمارهی تلفن تنها ورودیهای عددی را میپذیرد و ورودی کاربر نمیتواند شامل حروف باشد. به لطف ارائهي فناوریهایی نظیر HTML5، انواع ورودیها یا به عبارتی خصیصهی type در تگ input دیگر فقط شامل text و password نیستند و انواع مختلفی برای فیلدهای ورودی فرمها قابلاعمال است. به طور پیشفرض، هر نوع از آنها قالب مشخصی دارند و در صورتی که ورودی کاربر استانداردهای قالب مدنظر را در بر نگیرد، مواجهه با هشدار و خطا حاصل میشود.
در اینجا، نگاهی به انواع فیلدهای ورودی HTML5 در یک سامانه میاندازیم:
• text
• password
• number
• search
• tel
• url
• range
• datetime-local
• date
• month
• time
• week
• color
چرا اعتبارسنجی «منفی» نه؟
در اعتبارسنجی منفی، میتوانیم موارد خطرناکی مانند کدهای مخرب و اسکریپتهای XSS و یا حتی پرسش SQL را که کاربر میتواند در فرمها وارد کند، لیست کنیم و دریافت ورودیهای شامل آنها را در سامانه غیرمجاز تعیین کنیم و اینگونه از دریافت چنین مواردی به عنوان ورودی کاربر جلوگیری کنیم. البته این روش بسیار پرهزینه و زمانبر خواهد بود. در ضمن نکتهای که در این روش از آن غفلت شده است، خلاقیت هکر است. در این مواقع نباید خلاقیت هکر را دستکم گرفت چون امکان دورزدن موارد ممنوعه وجود دارد و احتمالا راههایی وجود داشته باشد که در لیست شما موجود نیستند. به همین دلایل، این راه، یعنی ایجاد یک BlackList از موارد ممنوعه، منطقی نیست و بهتر است در شرایط عادی از روشهای بهتر و مطمئنتری، همچون اعتبارسنجی مثبت استفاده شود.
عبارات منظم در اعتبارسنجی
در اینجا چند عبارت منظم را که میتوان برای کنترل دادههای ورودی در فیلدهایب مانند نام کاربری، پست الکترونیکی و شماره تلفن انجام داد، بررسی میکنیم:
این عبارت منظم میتواند برای نام کاربری بسیار مورداستفاده قرار بگیرد. در این عبارت منظم قوانینی برای ورود نام کاربری در یک فرم ثبتنام مشخص شده است و شما به عنوان برنامهنویس میتوانید هم در سمت کاربر و هم سمت سرور دادههای ورودی را بررسی و کنترل کنید.
همچنین، در عبارت منظم فوق محدودیتهای زیر برای نام کاربری ورودی کاربر تعیین شدهاند که طبق آنها:
• ورودی کاربر در فیلد نام کاربری باید بین ۸ تا ۲۰ کاراکتر باشد.
• ورودی کاربر در فیلد نام کاربری باید با نقطه شروع نشود.
این عبارت منظم برای پست الکترونیکی است.
در این عبارت منظم، الگوی یک پست الکترونیکی مشخص شده است و طبق آن:
• ورودی کاربر در فیلد ورودی پست الکترونیکی واردشده باید حتما @ داشته باشد.
• در ورودی کاربر، دامنهی میتواند شامل عدد و حرف باشد.
• ورودی کاربر در هر بخش، نباید کمتر از ۲ کاراکتر باشد وگرنه غیرمجاز شناخته میشود.
این عبارت منظم برای شماره تماس همراه اپراتورهای داخل ایران است که بر این اساس:
ورودی کاربر تنها در صورتی مجاز شناخته خواهد شد که شماره تلفن واردشده با +98 یا ۰۹ آغاز شود و بعد از آن ۹ رقم عدد وجود داشته باشد،
آیا میتوان ورودیها را به صورت خودکار نیز کنترل کرد؟
مواردی که ذکر شد مربوط به بهکارگیری عبارات منظم در کنترل ورودی کاربر بود. اما از راههای دیگری نیز میتوان برای کنترل ورودی کاربر کمک گرفت. راههایی نظیر استفاده از عبارات منظم یا RegExها و کتابخانههای اعتبارسنجی ارائه شده برای هر زبان برنامهنویسی است. کتابخانههایی در هر زبان برنامهنویسی نیز وجود دارند که میتوانند کنترل ورودیها را به دست بگیرند و کار را سادهتر کنند. دلیل پیشنهاد استفاده از این کتابخانهها، بهروزبودن آنهاست و این که اگر نقصی در آنها یافت شود معمولا سریعتر از حالت معمول، رفع میشود. البته ایمنی در استفاده از این کتابخانهها مشروط به این است که در هر زمان آخرین نسخهی هر کتابخانه بر روی سامانه نصب و استفاده شود.
برخی از کتابخانههای مشهور و پراستفاده برای کنترل ورودی کاربر، به تفکیک زبانهای برنامهنویسی از این قرارند:
در زبان برنامهنویسی Java:
• Hibernate (Bean Validation)
• ESAPI
در زبان برنامهنویسی ASP.NET:
• BaseValidator
در NodeJS:
• validator-js
بنابراین، طبق آنچه تا اینجا گفتیم:
• روش WhiteList انتخاب هوشمندانهتری برای کنترل ورودی کاربر به شمار میرود و بهتر است گزینهی اول ما باشد.
• تنها در مواقعی از راهکار BlackList برای ورودی کاربر استفاده کنید که راهکار WhiteList امکانپذیر نباشد.
• بهتر است ورودی کاربر را قبل از آن که بیشتر وارد منطق سامانه شوند، بررسی کنیم تا از اتفاقات بعدی مانند تزریق کد مخرب به منطق برنامه جلوگیری شود.
اهمیت S را در HTTPS دریابید!
در کنار اعمال محدودیت برای ورودی داده، لازم است که از سمت شما موضوع مهم دیگری نیز اعمال شود و آن، ایجاد حریم خصوصی و یکپارچگی دادهها در حین انتقال است. شما میتوانید کاربران خود را ملزم کنید که قوانین موجود را در ورود دادهها در فرمهای سامانهی شما رعایت کنند، اما نمیتوانید آنها را در محدودیت مکانی قرار دهید و اجازه ندهید که در جایی مانند رستوران با یک وایفای رایگان به سامانهی شما دسترسی پیدا کنند. زمانی که سامانه با استفاده از پروتکل HTTP دادهها را بین سامانه و کاربر منتقل میکند، دادهها در ترافیک بین سامانه و کاربر قابلمشاهده هستند. اگر مهاجمی توانایی مشاهدهی ترافیک بین کاربر و سامانه را داشته باشد، این امکان را خواهد داشت که اطلاعات لازم مانند توکنها و غیره را از این ارتباطات بردارد، به سامانه نفوذ کند یا دادههای ارسالی و دریافتی را در بین راه تغییر دهد و به مقصد روانه و یا سناریوهای دیگری از حملهی مرد میانی یا Man-In-The-Middle را اجرا کند.
برای رفع خطرات این حمله، لازم است که ارتباط بین کاربر و سامانه ناخوانا شود؛ بدین معنی که داده در سمت کاربر رمزگذاری، به سمت سامانه ارسال و در مقصد دوباره رمزگشایی شود و برعکس. به عنوان یک راهحل، پروتکل HTTPS برای ایجاد ارتباط امن و رمزشده ارائه شده است و این دغدغه را تا حد زیادی رفع میکند. این پروتکل ابتدا برای ترافیک سامانههای حساسی مانند سامانههای بانکی ارائه شد، اما اکنون بر پایهی پروتکل TLS و SSL قابل راهاندازی بر روی هر سامانهای است.
بنابراین با پروتکل HTTPS اگر کاربر سامانه در محلی باشد که ترافیک او با سامانه برای مهاجم قابل مشاهده باشد، دادههای ارسالی و دریافتی بین کاربر و سامانه برای مهاجم به صورت ناخوانا هستند. به این دلیل است که، استفاده از پروتکل HTTPS بسیار ایمنتر از پروتکل HTTP، خواهد بود.
چگونه میتوان HTTPS را به سامانه اعمال کرد؟
راهاندازی HTTPS در سامانه نیازمند گواهینامهای است که معمولا توسط نمایندههای صادرکنندهی این گواهینامهها صادر میشوند. این گواهینامه شامل یک کلید عمومی و غیرمحرمانه است که نشان میدهد این سامانه مالک گواهینامه است و همچنین شامل یک کلید خصوصی و محرمانه است. در بعضی سامانههای حساس تنها در صورت ارائهی کلید خصوصی توسط کاربر مجوز ورود به سامانه صادر میشود. البته به علت پیچیدگی مدیریت گواهینامهها برای کاربران همان سامانه، استفاده از این قابلیت به شدت نادر است.
این گواهینامه معمولا رایگان نیست اما با ظهور سامانهی Let's Encrypt! گرفتن گواهینامهی رایگان فراهم شده است.
دقت کنید که اگر پیکربندی پروتکلهای مربوط به HTTPS نادرست و دارای نقص باشد، خود به تنهایی باعث بروز آسیبپذیریهای مختلف دیگری نیز میشود. شما میتوانید با مراجعه به آزمایشگاه SSL پیکربندیهای خود را بسنجید و در صورتی که این آزمایشگاه نقصی در پیکربندیهای سامانه شما مشاهده و به شما اعلام کرد، آن را رفع کنید. بهتر است این کار را هر چند ماه یکبار تکرار کنید.
آیا کنترل دادههای ورودی و استفاده از پروتکل HTTPS برای جلوگیری از نفوذ کافیست؟
میتوان در یک کلام گفت: "خیر". اگر تعداد ثبت درخواستها در فرمهای ورودی کنترل نشوند و امکان ارسال اطلاعات به تعداد زیاد موجود باشد، هکر این امکان را خواهد داشت که با به راهانداختن سیلی از درخواستها به سمت سرور، سامانه را به طور کامل در لحظاتی مختل کند و از کار بیندازد. با این کار، هکر حملهی منع دسترسی به سرویس یا Denial-of-Service را بر روی سامانه پیاده میکند و برای نمونه این امکان را خواهد داشت که در یک صفحهی لاگین به تعداد بسیار زیاد نامهای کاربری و گذرواژههای مختلفی را امتحان و در واقع حملهی BruteForce را پیاده کند. حملهی منع دسترسی به سرویس فقط شامل مختلکردن کارکرد سامانه نیست و هکر با ارسال تعداد بسیار زیادی درخواست با ایجاد اختلال در سامانه میتواند سرور را اصطلاحا گیج کند تا درخواستهای بدخواهانه از سوی هکر را به عنوان یک درخواست عادی پردازش کند. به عنوان نمونه هکر مشخصات یک کاربر را در یک فرآیند فراموشی گذرواژه تغییر دهد و با جعل حساب کاربری قربانی وارد سامانه شود.
برای جلوگیری از این حمله، میتوان چند راه را در نظر گرفت:
• استفاده از تایمر برای هر بار ارسال درخواست از سمت کاربر
• استفاده از کپچا
• درخواست گذرواژه برای فرآیندهای حساسی مانند فراموشی گذرواژه
استفاده از تایمر برای هر بار ارسال درخواست از سمت کاربر
در این روش، از ثبت فرم به طور پیاپی توسط کاربر جلوگیری میشود، اما چگونه؟ برنامهنویس میتواند با قراردادن زمان پس از ارسال دادههای فرم، امکان ثبت مجدد فرم را تا لحظاتی غیرفعال کند تا درخواستها به صورت سیل به سمت سرور روانه نشوند.
استفاده از کپچا
در گذشته کپچا به صورت یک تصویر با نوشتههایی که خواندن آنها سخت بود، مورد استفاده قرار میگرفت که این امکان علاوه بر اینکه از وجود یک کاربر غیرانسان در سمت کاربر تا حد زیادی جلوگیری میکرد، بین دو درخواست متوالی کاربر نیز فاصلهی زمانی ایجاد میکرد. کپچاهای هوشمند امروزی با استفاده از مکانیزمها و سوالاتی از جمله انتخاب تصاویر مرتبط به سوال و غیره، این قابلیت را بهتر از کپچاهای قدیمی فراهم کردهاند.
درخواست ورود دوبارهی گذرواژه برای فرآیندهای حساسی مانند فراموشی گذرواژه
این روش هم با فاصلهانداختن بین دو درخواست پیاپی، میتواند راهحل بسیار خوبی باشد. مزیت دیگر این روش، بررسی و اعتبارسنجی کاربر پیش از هر فرآیند مهم و حساس است تا از هویت کاربر اطمینان حاصل شود.
سخن آخر
نکاتی که با هم بررسی کردیم شامل مفاهیم و راههای جلوگیری از نفوذ هکرها از طریق ورودیهای سامانه بودند، به بیان این پرداختیم که اگر دادههای ورودی به صورت صحیح کنترل نشوند، امکان بروز آسیبپذیریهای مختلف برای هکرها فراهم میشود و این آسیبپذیریها میتوانند دروازههای ورودی برای هکرها باشند. بنابراین، باید نکات مطرحشده را بسیار جدی گرفت. ما در دو مطلب «SQL Injection؛ راهبردها و ترفندها» و «آسیبپذیری XXE؛ نفوذ با جابجایی دادهها» به نکاتی برای شکارچیها برای نفوذ پرداختیم و راههای جلوگیری از بروز آنها را بررسی کردیم که اغلب آن نکات و ترفندها میتوانند از طریق ورودیهای سامانه در فرمها انجام شوند و شاید بتوان گفت که اغلب مشکلات امنیتی سامانهها در درجهی اول مربوط به عدم کنترل ورودیهای سامانهها هستند. امیدواریم که شما به عنوان یک برنامهنویس یا توسعهدهنده، با کنترل فرمهای پروژههای خود گام مهمی در جهت ایجاد امنیت بیشتر بردارید.
منابع:
https://martinfowler.com/articles/web-security-basics.html
https://developer.mozilla.org/en-US/docs/Learn/Forms/HTML5_input_types