خدمات الويب : مقدمة ( الجزء الأول )

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

Capture

الصورة في الأعلى على سبيل المثال توضح كيف يستطيع تطبيق OneDrive التواصل مع Microsoft word من أجل تفعيل خيار تعديل المستند داخل برامج الـ Word الموجود لدى العميل , و السؤال هنا كيف عرف OneDrive أصلا بأن برنامج word مثبت على جهاز العميل ؟ و كيف استطاع الـ OneDrive تشغيل التطبيق اصلا و هو موجود على القرص الصلب الخاص بي و ليس له أي علاقة بالخادم .

تمكن الفكرة بأكملها في تثبيت عميل على الكمبيوتر الخاص بالعميل و من ثم يقوم تطبيق الويب الخاص بك الموجود على الخادم بتحدث مع هذا العميل, في مثالي هنا سأتحدث عن WCF كعميل ممتاز هو لا يحتاج إلى واجهة و من الممكن أن يعمل كتطبيق Console أو كـ Windows service أو حتى كتطبيق ويبب مستضاف على IIS . 

WP_20141028_001

اذا نظريا يقوم العميل بطلب صفحة الويب حيث تقوم صفحة الويب بطلب خدمة WCF الموجودة داخل جهاز العميل , عندها تقوم خدمة WCF بتواصل مع الجهاز المثبت على كمبيوتره , يقوم الجهاز بعمله و ارسال البيانات من خلال مكتبة DLL الخاصة به و بعد الانتهاء أو خلال عمله يقوم بتواصل مع الخادم و ارسال البيانات له و التي اخيرا بعد هذه الرحلة يتم عرضها على صفحة asp.net  , الفيديو:

إذا خدمات الويب انها اكثر بكثير من مجرد ملفات XML او JSON الأمر لا يقتصر فقط على تبادل الملفات او المعلومات بين التطبيقات اليس كذلك , سأحاول من خلال سلسلة جديدة من المقالات تسليط الضوء على خدمات الويب بنوعيها Resut  و SOAP و كيفية استخدامها و التعامل معها من خلال C# سواء باستخدام WCF او Web service  .

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

تحدثنا في المقالة السابقة عن حماية ملف Web.config و كيف يتم تحميل هذا الملف الذي يعتبر القلب النابض لمشاريع asp.net او MVC في هذه المقالة سنتحدث عن جزء أخر خطئ جسيم يرتكب كثيرا في تطبيقات في asp.net و هو عملية ادارة الجلسات الخاصة بـ asp.net  .

إذا كنت أحد المطورين الذين يستخدم الـ View state كوسيلة لحفظ البيانات خلال جلست العميل فأرجوك توقف فورا عن عمل ذلك , إن استخدام الـ View state كوسيلة من أجل حفظ مثلا كلمات المرور أو اسم المستخدم أو بيانات تحتاجها أثناء العمل على التطبيق أمر  لا يمكن غفرانه لسبب بسيط جدا لأن الـ View State يمكن اختراقها ؟

الفيديو التالي يعبر عن مليون كلمة انه يوضح بشكل مباشر كيف يمكن فك تشفير الـ View State لنى عودة بعد الفيديو :

لذلك ارجوك رجاء خاص توقف عن استخدام viewstat فورا .

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

تحدثنا في المقالة السابقة عن ثغرة الـ Bliend Direction و رئينا كيف يتم استخدام عدم التحقق من وجود الصفحة المراد التحويل عليها إلى الية لسرقة بيانات المستخدم , في هذه المقالة سنتحدث عن تشفير ملف الـ web.Config , يعتبر ملف الـ Web.Config اكثر الملفات أهمية على الاطلاق في التطبيق فهو يحتوي على معلومات كثير حول الية عمل التطبيق بدأ من نص الاتصال بقاعدة البيانات مرورا بـ روابط الاتصال بخدمات الويب او اعدادات التواصل مع خادم البريد الكتروني وصولا إلى إعدادات التطبيق الخاصة , بشكل أخر يعتبر هذا الملف القلب النابض لجميع تطبيقات asp.net بمختلف انواعها سواء MVC او asp.net , لذلك يجب حماية هذا الملف بشكل جيد .

لفترة طويلة ادعت السيدة العجوز Microsoft  بأنه لا يمكن نهائيا تحميل هذا الملف و ان تحميله يعتبر امرا مستحيلا و في الأحلام , أطمن المطورين كثيرا لهذا الامر حتى خرجت علينا ثغرة Padding Oracle و التي استخدمت بأشكال مختلفة حتى استطاع المهاجمين اخيرا استخدماها من أجل تحميل ملف الـ web.config , الفيديو يشرح بطريقة رائعة الية عمل الثغرة :

صدق او لا تصدق مرة على الثغرة الأن ما يقارب الثلاث سنوات و مازال ما يقارب 70% من تطبيقات asp.net المنشوره على الويب تحمل هذه الثغرة :-) , المسبب الرئيسي لهذه المشكلة هو شخص واحد فقط , و هو الـ Web Administrator و على الرغم من أن الخطأ يقع عليه بالكامل إلى أن هذا الأمر لا يعني تجاهل المطور لتطبيق من حماية تطبيقه .

الحل

هناك ثلاثة خطوات لحل هذه المشكلة على الشكل التالي :

1- معالجة الاخطاء كما رئينا سابقا في الجزء الثالث .

2- تحميل تحديث MS10-070 و ثتبته على خادم الـ Host لتطبيق . 

3- تشفير ملف الـ Web.config دائما و ابدأ شفر ملف الـ Web.config و لا تتركه عرضة للمخاطر.

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

تطبيقات أكثر أمانا مع 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 عندها اصبحت في ورطة كبيرة جدا يا صديقي .