تطبيقات أكثر أمانا مع asp.net ( الجزء الخامس ).

حسنا تحدثنا في المقالة السابقة عن كيفية استغلال التحقق من جهة العميل فقط او من جهة الخادم فقط كثغرة يمكن فيها تحقيق هجمتي الـ DOS و هجمة Data Bag في هذه المقالة سنتحدث عن نوع اخر من الهجمات هو ما يعرف blind Redirect كيف يتم و ما هو الخطر الواقع عليه .

ما هو الـ Blind Redirect ؟

تحدث هذه الثغرة كثيرا في صفحات تسجيل الدخول خصوصا , تقوم بشكل طبيعي ببناء تطبيق اي كان حيث يتطلب هذا التطبيق ان يكون المستخدم قام بتسجيل الدخول أولا و في حالت عدم تسجيل الدخول يقوم التطبيق الخاص بك بتوجيه المستخدم لصفحة تسجيل الدخول لتسجيل الدخول و اذا كانت بيانات تسجيل الدخول صحيحة يقوم التطبيق بتحويله إلى الصفحة السابقة , على حسب الشكل التالي :

image

كما تلاحظ في الشكل الذي في الأعلى سيظل المستخدم في عملية Loop حتى يقوم بتسجل دخول صحيح على النظام هنا يأتي دور المهاجم فهمته الأن هو كسر هذه الحلقة المتعلقة بتسجيل الدخول , غالبا ستقوم بكتابة رابط مشابه لتالي :

http://localhost/Pages/Login.aspx?RedirectTo=Default.aspx

و حتى يقوم المهاجم بكسر هذه الحلقة فهو يقوم تحويل قيمة المتغير RedirectTo إلى صفحة تسجيل دخول مشابه تماما لتلك الموجودة في التطبيق الخاص بك مستغلا بذلك كون التطبيق الخاص بك يقوم بتوجه المستخدم إلى اي صفحة موجودة في المتغير RedirectTo صفحة تسجيل الدخول الخاص به ستحتوي على رسالة تخبره عفوا بيانات تسجيل الدخول غير صحيحة رغم أنها صحيحة في صفحتك الحقيقة , يقوم المستخدم الأن بكتابة بيانات تسجيل الدخول مرة أخرى و عندها يرسل بياناته للمهاجم في هذ الوقت يقوم المهاجم بتحويل المستخدم إلى صفحتك الحقيقة وهي Default  , الشكل :

image

إذا ما الخطأ الذي وقع به المطور و الذي جعل المهاجم يستغل هذه الثغرة في التطبيق ؟ بكل بساطة لا يقوم المطور العبقري بتحقق أصلا من أن الصفحة التي يتم التوجه إليها ضمن صفحات التطبيق :-), صدق أو لا تصدق حل هذه المشكلة سطر واحد فقط من الشفرة :

Capture

فقط سطر واحد كفيل بحماية المستخدمين لتطبيق , اذا في المرة القادمة فكر جديا في عملية تحويل المستخدمين إلى صفحات داخل التطبيق قبل عملية التجويل .

تطبيقات أكثر أمانا مع asp.net ( الجزء الرابع ).

حسنا تحدثنا في المقالة السابقة عن كيف يمكن استغلال المعلومات التي تنتج عن الاخطاء في تجميع معلومات كثيرة متعلقة بالتطبيق الخاص بك, في هذه المقالة سنتحدث عن التحقق من البيانات و كيف يتم الأمر و ايهما أصح هل التحقق على مستوى العميل ام على مستوى التطبيق  و لماذا التحقق شيء مهم للغاية .

 أيهما أفضل التحقق على مستوى العميل أم على مستوى الخادم ؟

يقصد بالتحقق على مستوى العميل هو التحقق باستخدام ادوات asp.net Validation  المختلفة , سواء التحقق من اختيار القيم أو التأكد من موجود نص أو مطابقة نص أو أن جميع الحقول غير فارغة أو أن حقل معين مكتوب بالحروف العربية و حقل أخر فقط أرقام , اذا كنت لا تعرفها فأنصحك بالطلاع عليها, ما تقوم بعمله هذه الأدوات في النهاية هو توليد شفرة Java Script على المتصفح بحيث أن يتم التحقق من المدخلات من جهة العميل,

إذا السؤال الذي يطرح نفسه الأن أيمها أفضل التحقق عند العميل أم عند الخادم , لنبدأ بالتحقق عند العميل , بدء مطور في عالم الا نهائية و ما بعدها بتطوير صفحة لتسجيل الدخول و اعتمد في تطويره على الـ Java Script فقط حيث يعتقد بأن التحقق في جهة الخادم أمر سيولد الكثير من الضغط على الخادم خصوصا اذا علمت أن شريحة المستخدمين للتطبيق من كبار السن  ,هذه صفحة التسجيل التي قام بكتابتها :

   1: <html dir="rtl" xmlns="http://www.w3.org/1999/xhtml">
   2: <head id="Head1"><title>
   3:     تسجيل الدخول
   4: </title><link href="../Content/themes/css/styles.css" rel="stylesheet" /><link href="../Content/themes/css/OutsideStyle.css" rel="stylesheet" /></head>
   5: <body style="font-family: Segoe UI,Tahoma;">
   6:     <form method="post" action="Login.aspx" onsubmit="javascript:return WebForm_OnSubmit();" id="form1">
   7: <div class="aspNetHidden">
   8: <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
   9: <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
  10: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="idJkK9PUISmEQbqWHACHhBNFtUTpdu6urCLR1kaCoFxYF+xsiaesMzAsIucuXQ6DIcfWMbX4W9w5aslOEvIV8V8vXxc=" />
  11: </div>
  12:  
  13: <script type="text/javascript">
   1:  
   2: //<![CDATA[
   3: var theForm = document.forms['form1'];
   4: if (!theForm) {
   5:     theForm = document.form1;
   6: }
   7: function __doPostBack(eventTarget, eventArgument) {
   8:     if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
   9:         theForm.__EVENTTARGET.value = eventTarget;
  10:         theForm.__EVENTARGUMENT.value = eventArgument;
  11:         theForm.submit();
  12:     }
  13: }
  14: //]]>
</script>
   1:  
   2:  
   3:  
   4: <script src="/fst/WebResource.axd?d=pynGkmcFUV13He1Qd6_TZO64iaE_OkdKVTpEiILH6N1c33ohKrJ7hFSz08e99aRhPsD_zQ2&amp;t=635307447758913288" type="text/javascript">
   1: </script>
   2:  
   3:  
   4: <script src="/fst/ScriptResource.axd?d=nv7asgRUU0tRmHNR2D6t1Dux76xlIi7f83w_688tepodWxez-u-CcZkDZbPry2Hd1kJiV9xPAdUhw7OZo-DS3Pqo9C7VSTxRCq5beqX8SjDAaiMs2G9E5F5e1R2thU8yoX4PMg2&amp;t=fffffffff284add6" type="text/javascript">
   1: </script>
   2: <script src="/fst/ScriptResource.axd?d=x6wALODbMJK5e0eRC_p1LXycwo8ZQyYZgbgto6ZUB-CrilT9kqTnn-emreMqKJOopRfn3UM89d7gI-RTDg4-0VISQtma5hdoWGrbSAoRHYVdVQVn0&amp;t=254cc609" type="text/javascript">
   1: </script>
   2: <script src="/fst/ScriptResource.axd?d=P5lTttoqSeZXoYRLQMIScHvAkR0rY-oBcsfr8DbT_PGW42Z5Qk7aYmRe_UGM6988IB5SSTE7eLpP2NHppNAi5BQHA7EHN7YmgWeQkfB-8P6S-xxT0&amp;t=254cc609" type="text/javascript">
   1: </script>
   2: <script src="/fst/ScriptResource.axd?d=NHo7rzvB81m25b5lv-ojMTdLePEV-yky08gdCbqiulz_fJ-2sQiPVuYDvHiDhRQUz-moqlZrdLFfOEWXmOiXGwFUMDNAfYqWmj-yDspI-D1xCSZ70&amp;t=254cc609" type="text/javascript">
   1: </script>
   2: <script src="/fst/ScriptResource.axd?d=G7OZOzpYF9XwHB8eN29YbyoOwH8D541dBCBzyrIO_k6VjOVh7f8qrOw2P4kAVCNqEoD7hg9eCjYhfyt8MiQ7556gaKS5ZwQL4s9-BZ64FsVXBj-YDnOyZwz_XR5Pz2gsf8Lj1w2&amp;t=254cc609" type="text/javascript">
   1: </script>
   2: <script src="/fst/ScriptResource.axd?d=Y4RJC2UgV6Ws3MXxk43ByaIRTFKC3jcuQTkPlbnVF1Uf1Q_N_dDaXH50I6AaOHV-Fiy0GulKIlbEaDlYm24we0Z3eZl1JgM5mEGsvPfvgJTA_J9G_grOv_Vyk0h10e5QUoK4jQ2&amp;t=254cc609" type="text/javascript">
   1: </script>
   2: <script type="text/javascript">
   3: //<![CDATA[
   4: function WebForm_OnSubmit() {
   5: null;if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) return false;
   6: return true;
   7: }
   8: //]]>
</script>
   1:  
   2:  
   3: <div class="aspNetHidden">
   4:  
   5:     <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="NAV27dyxA1d6fDV0in9fdlmULc7NKjhR2VBdl0pY6uBuzauevmHNuwKFfY9L6aiG/WV611xCeTdafgJAj4BuCBa7DDsgSDQhnKbCPYmaMz2Z4IyQJIazLt44gaO/IPgHUs8SnkjFlbGYB3o3dh5Cz96Z3cmrflLT5PYTZYvMRSGsUQFjESoYxP/XCqDEYiQBqvBXJa9J7/tc1Y9RB1z6Pfw9eVP6nyDG82AR8yTcLRaP02io" />
   6: </div>
   7:         <script type="text/javascript">
   8: //<![CDATA[
   9: Sys.WebForms.PageRequestManager._initialize('ScriptManager1', 'form1', [], [], [], 90, '');
  10: //]]>
</script>
   1:  
   2:  
   3:         <div style="margin: 100px auto; padding: 40px; border: 12px solid rgb(215, 215, 215); width: 350px; margin-top: 15%;">
   4:             <table style="margin-right: 40px;">
   5:                 <tr>
   6:                     <td style="width: 300px;">
   7:                         <table>
   8:                             <tr>
   9:                                 <td style="text-align: right; font-size: small;">ادخل كلمة المرور و اسم استخدم
  10:                                 </td>
  11:                             </tr>
  12:                             <tr>
  13:                                 <td style="text-align: left;">
  14:                                     <table cellpadding="1" cellspacing="0" style="border-collapse: collapse;">
  15:                                         <tr>
  16:                                             <td>
  17:                                                 <table cellpadding="0">
  18:                                                     <tr>
  19:                                                         <td align="left">
  20:                                                             <input name="UserName" type="text" id="UserName" dir="ltr" style="height:25px;width:250px;" />
  21:                                                             <input type="hidden" name="txtbwUserName_ClientState" id="txtbwUserName_ClientState" />
  22:                                                             <span id="UserNameRequired" title="اسم المستخدم مطلوب" style="visibility:hidden;">*</span>
  23:                                                         </td>
  24:                                                     </tr>
  25:                                                     <tr>
  26:                                                         <td align="left">
  27:                                                             <input name="Password" type="password" id="Password" dir="ltr" style="height:25px;width:250px;" />
  28:                                                             <input type="hidden" name="txtbwPassword_ClientState" id="txtbwPassword_ClientState" />
  29:                                                             <span id="PasswordRequired" title="كلمة المرور مطلوبة" style="visibility:hidden;">*</span>
  30:                                                         </td>
  31:                                                     </tr>
  32:                                                     <tr>
  33:                                                         <td>
  34:                                                             <table>
  35:                                                                 <tr>
  36:                                                                     <td>
  37:                                                                         <input name="txtCaptcha" type="text" id="txtCaptcha" dir="rtl" style="height:25px;width:150px;" />
  38:                                                                         <input type="hidden" name="txtbwCaptch_ClientState" id="txtbwCaptch_ClientState" />
  39:                                                                         <span id="rfvCaptch" title="نص التحقق مطلوب" style="visibility:hidden;">*</span>
  40:                                                                     </td>
  41:                                                                     <td style="vertical-align: bottom; text-align: right;">
  42:                                                                         <img id="Image1" src="Captcha.aspx" />
  43:                                                                     </td>
  44:                                                                 </tr>
  45:                                                             </table>
  46:                                                         </td>
  47:                                                     </tr>
  48:                                                     <tr>
  49:                                                         <td align="center" style="color: Red;">
  50:                                                             
  51:                                                         </td>
  52:                                                     </tr>
  53:                                                     <tr>
  54:                                                         <td align="left">
  55:                                                             <div style="margin-left: 10px" />
  56:                                                             <table width="100%">
  57:                                                                 <tr>
  58:                                                                     <td align="right">
  59:                                                                         <div style="font-size: small;">
  60:                                                                         </div>
  61:                                                                     </td>
  62:                                                                     <td align="left">
  63:                                                                         <input type="submit" name="btnLogin" value="دخول" onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(&quot;btnLogin&quot;, &quot;&quot;, true, &quot;Login1&quot;, &quot;&quot;, false, false))" id="btnLogin" class="button" />
  64:                                                                     </td>
  65:                                                                 </tr>
  66:                                                             </table>
  67:                                                         </td>
  68:                                                     </tr>
  69:                                                 </table>
  70:                                             </td>
  71:                                         </tr>
  72:                                     </table>
  73:                                 </td>
  74:                             </tr>
  75:                         </table>
  76:                     </td>
  77:                 </tr>
  78:             </table>
  79:         </div>
  80:     
  81: <script type="text/javascript">
  82: //<![CDATA[
  83: var Page_Validators =  new Array(document.getElementById("UserNameRequired"), document.getElementById("PasswordRequired"), document.getElementById("rfvCaptch"));
  84: //]]>
</script>
   1:  
   2:  
   3: <script type="text/javascript">
   4: //<![CDATA[
   5: var UserNameRequired = document.all ? document.all["UserNameRequired"] : document.getElementById("UserNameRequired");
   6: UserNameRequired.controltovalidate = "UserName";
   7: UserNameRequired.errormessage = "اسم المستخدم مطلوب";
   8: UserNameRequired.validationGroup = "Login1";
   9: UserNameRequired.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
  10: UserNameRequired.initialvalue = "";
  11: var PasswordRequired = document.all ? document.all["PasswordRequired"] : document.getElementById("PasswordRequired");
  12: PasswordRequired.controltovalidate = "Password";
  13: PasswordRequired.errormessage = "Password is required.";
  14: PasswordRequired.validationGroup = "Login1";
  15: PasswordRequired.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
  16: PasswordRequired.initialvalue = "";
  17: var rfvCaptch = document.all ? document.all["rfvCaptch"] : document.getElementById("rfvCaptch");
  18: rfvCaptch.controltovalidate = "Password";
  19: rfvCaptch.errormessage = "نص التحقق مطلوب";
  20: rfvCaptch.validationGroup = "Login1";
  21: rfvCaptch.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
  22: rfvCaptch.initialvalue = "";
  23: //]]>
</script>
   1:  
   2:  
   3:  
   4: <script type="text/javascript">
   5: //<![CDATA[
   6:  
   7: var Page_ValidationActive = false;
   8: if (typeof(ValidatorOnLoad) == "function") {
   9:     ValidatorOnLoad();
  10: }
  11:  
  12: function ValidatorOnSubmit() {
  13:     if (Page_ValidationActive) {
  14:         return ValidatorCommonOnSubmit();
  15:     }
  16:     else {
  17:         return true;
  18:     }
  19: }
  20:         Sys.Application.add_init(function() {
  21:     $create(Sys.Extended.UI.TextBoxWatermarkBehavior, {"ClientStateFieldID":"txtbwUserName_ClientState","WatermarkText":"اسم المستخدم","id":"txtbwUserName"}, null, null, $get("UserName"));
  22: });
  23:  
  24: document.getElementById('UserNameRequired').dispose = function() {
  25:     Array.remove(Page_Validators, document.getElementById('UserNameRequired'));
  26: }
  27: Sys.Application.add_init(function() {
  28:     $create(Sys.Extended.UI.TextBoxWatermarkBehavior, {"ClientStateFieldID":"txtbwPassword_ClientState","WatermarkText":"password","id":"txtbwPassword"}, null, null, $get("Password"));
  29: });
  30:  
  31: document.getElementById('PasswordRequired').dispose = function() {
  32:     Array.remove(Page_Validators, document.getElementById('PasswordRequired'));
  33: }
  34: Sys.Application.add_init(function() {
  35:     $create(Sys.Extended.UI.TextBoxWatermarkBehavior, {"ClientStateFieldID":"txtbwCaptch_ClientState","WatermarkText":"نص التحقق","id":"txtbwCaptch"}, null, null, $get("txtCaptcha"));
  36: });
  37:  
  38: document.getElementById('rfvCaptch').dispose = function() {
  39:     Array.remove(Page_Validators, document.getElementById('rfvCaptch'));
  40: }
  41: //]]>
</script>
  14: </form>
  15: </body>
  16: </html>

صفحة تسجيل دخول بسيطة لا يوجد بها شيء عظيم , تقوم هذه الشاشة بتحقق من بيانات تسجيل الدخول عند العميل باستخدام ادوات التحقق في asp.net او JQuery لا يفرق الامر كثيرا و بالتالي حقق المهندس عطية مبتغاة و اراح الخادم الخاص به كثيرا .


في نفس هذه الأثناء هناك مطور أخر في الجهة الأخرى من العالم يعمل على مشروع عظيم أخر هذا المطور اعتمد في التحقق على الخادم فقط و لم يقم بكتابة سطر واحد من أسطر Java Script حيث يعتقد  بأن التحقق عن طريق الخادم افضل و ادق من التحقق عن طريق Java Script .


في الحقيقة كلاهما صح و كلاهما خطأ في نفس الوقت , في حين أن التحقق من جهة الخادم ادق و اكثر فاعلية من التحقق من جهة العميل إلى انه يستنزف كثيرا من قدرات الخادم و في حين أن التحقق من جهة العميل فقط من شأنه أن يخفف هذا الضغط إلى أن الأمر لا يخلو من مخاطر لنرى الأن مخاطر استخدام أحد الحلين دون الأخر :


استخدام Java Script فقط


ما سيحدث الأن يقوم المهاجم تثبيت اضافة إلى متصفح Googl Chrome تقوم بتعطيل الـ Java Script مثلا اذا كان المطور عبقريا للغاية و قد اكتفى بالتحقق من جهة العميل فعندها قد تجاوزت التحقق و دخلت على الصفحة التالية .


من جهة الخادم فقط


في هذه النوع يمكن عمل ما يعرف  Auto Request و هي برامج كثيرة موجودة على الويب تعطيها رابط لموقع معين فتقوم الاخيرة بتوليد كم كبير من الـ Post Back او Submit خلال ثواني معدودة و كون الصفحة لا تحتوي على Java Script نهائيا فهذا القدر الهائل من العمل سيكون ملقى كليا على عاتق الخادم و مع مرور الوقت سيحدث لديك ما يعرف بـ Denail of service attack حيث أن هدف المهاجم ليس اختراق التطبيق انما قلته نهائيا .


الحل الصحيح


دائما و أبد تحقق من جهة العميل و من جهة الخادم أيضا لماذا لسبب بسيط فإن المستخدمين غير الجديين لا يشكلون أكثر من 1% من مستخدمين التطبيق في بالتالي فعليا جميع جمل if يستم تجاوزها بسرعة دون أي تأثير يذكر إذا كان هناك تحقق من جهة العميل . أضف إلى أن التحقق من جهة الخادم سيمنع تلك الطلبات التي لا تحترم الـ JS .


سيحميك التحقق من جهة العميل من ما يهرف  بـ DOS او Denail of service attack و له أشكال كثيرة و أحد أشكالها ضرب الخادم كليا عن طريق توليد كما هائل من الطلبات على الخادم ليست مهمتك كمطور ويب حظر عناوين الـ IP فهذه مهمة System Admin و الفريق الأمني و لكن مهمتك هي أن التحمي التطبيق الخاص بك عن طريق تفعيل الـ JS و التي من شأنها أن تمنع عمليات الـ  Post Back العشوائية.


الثاني ما يعرف بـ Data Bag  حيث تتحول قاعدة البيانات الخاصة بتطبيقك إلى مكب للنفايات بمعنى عدم وجود تحقق نهائيا من جهة الخادم يعني بأن المهاجم سيقوم بتوليد بيانات عشوائية كليا على شرط أن تتوافق مع شروط Java Script ,  و اضافتها إلى قاعدة البيانات الخاصة بك حتى تمتلئ كليا بالبيانات فإذا كنت في استضافة مثلا يجب أن لا يتجاوز قاعدة البيانات فيها اكثر 500M عندها اصبحت في ورطة كبيرة جدا يا صديقي .