22-09-20, 07:37 PM
(آخر تعديل لهذه المشاركة : 22-09-20, 07:54 PM {2} بواسطة Anas Mahmoud.)
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاتة
لتذكير نفسي واياكم اكتب هذا الموضوع لتوضيح نقطة هامة جدا تتعلق ببناء الاستعلامات الديناميكية داخل البرنامج ،لكي يكون الكل على دراية بها
تقديم :
حينما تختار ان تربط برنامجك بقاعدة بيانات يجب ان تعلم انك المسئول الوحيد عن امان هذه البيانات ، واعني بكلمة امان هنا الخصوصية ، ان لايطلع احد غير مصرح له على بيانات لا يجب ان يراها ، وكذلك اعني الاستمرارية ، ان لايحدث ان تفقد بياناتك او جزء منها
واحدة من اكبر نقط الضعف vulnerability عند بناء استعلامات في الكود هو تمرير بيانات من المستخدم مباشرة الى نص الاستعلام ، يجب ان تستحضر دائما ان نص الاستعلام ما هو الا نص String ، اذا اُدخل فيه نص استعلام اخر صالح سيقبله محرك قاعدة البيانات ايضا
حينما تضع مربع نص في البرنامج ، تأخذ منه المدخل ، وتضعه مباشرة في الاستعلام ، فانت فعليا فتحت نافذه من مربع النص هذا الى قاعدة البيانات ، اذا كان المستخدم جيدا بما فيه الكفاية يمكنه بخطوات غاية البساطة كما سأشرح في هذا الموضوع فعل كل شيء بقاعدة بياناتك ، بما يتضمنه سرقة البيانات او حتى ضياعها
اذا كنت لازلت تبني نص الاستعلام بهذه الطريقة فانت بحاجة الى ان تتوقف عن ذلك فورا ،
للتجربة ، سنحاكي نظام حسابات بسيط به فورم تسجيل دخول وفورم الحسابات وقاعدة بيانات بها جدول للمستخدمين وجدول للحسابات
انشأ مشروع جديد ، عدل فورم تسجيل الدخول الى الشكل التالي :
وفورم الحسابات :
جدول المستخدمين :
وضعنا به بيانات مستخدمين للتجربة :
جدول الحسابات :
تصميم برنامج ضعيف ضد SQL INJECTION :
حقن الاستعلامات او Sql Injection هي طريقة لمهاجمة قواعد البيانات عن طريق استغلال المدخلات الغير محمية ، بحيث تقوم بادخال في نص الاستعلام ما يقوم بتغير وظيفته او اضافة وظيفة جديده له تخدم المهاجم
في برنامجنا البسيط نريد ان نتأكد من ان المستخدم الذي يحاول تسجيل الدخول موجود فعلا في جدول المستخدمين ، وربما ايضا التأكد من صلاحياته
لفعل ذلك نقوم بعمل استعلام يعود باسم المستخدم في حال تطابق اسم المستخدم المدخل مع احد اعمدة جدول المستخدمين وكذلك كلمة السر المدخلة مع نفس العمود
(وجب ان انبه مرة اخرى ان هذه هي الطريقة الخاطئة ، للتجربة فقط)
يكون نص الاستعلام :
ثم نكمل باقي الكود لاتمام العملية ، ExecuteScalar تعود باول خلية في اول صف وهذا ما نريده :
الان نختبر اذا كان UserName يحمل قيمة هذا معناه ان المستخدم موجود فعلا ، وبالتالي نفتح الفورم الرئيسي ونمرر له اسم المستخدم :
كود بسيط جدا ،
الان شغل البرنامج واكتب اسم مستخدم خاطيء :
الان اكتب اسم مستخدم وكلمة سر من جدول المستخدمين فعلا :
ممتاز جدا الى الان لا مشاكل ولكن انتظر
مهاجمة البرنامج باستخدام SQL INJECTION:
تعطيل العمل:
طيب جرب افتح البرنامج من جديد وادخل الرمز ' فقط في اسم المستخدم واضغط تسجيل الدخول :
ينهار البرنامج ويعطيك الخطأ التالي :
ما معنى ذلك ؟ يخبرك البرنامج ان هناك قوس نص ' غير مغلق ، ادركت حجم المصيبة ؟ قاعدة البيانات اعتبرت مدخل المستخدم جزء من الاستعلام وليس مجرد مدخل
تخطي التسجيل :
هل هذا اسوأ ما قد يحدث ؟
بالطبع لا ، من الممكن ببساطة في مثالنا ان نتخطى عملية التسجيل اصلا ،
افتح البرنامج من جديد و ادخل الجملة التالية في مربع اسم المستخدم ، وهي الاشهر فيما يتعلق بال SQL INJECTION :
ثم اضغط تسجيل دخول
دخلنا فعلا دون اسم مستخدم او كلمة سر ! كيف حدث هذا
دعنا نراجع نص الاستعلام الذي استخدمناه في حال ادخال اسم المستخدم : user1 وكلمة السر : 12345678
طبيعي جدا ، طيب ماذا يحدث في حالة ادخال :
' نغلق بها قوس النصوص المفتوح و OR 1=1 نجعل بها الشرط متحقق دائما و -- تقوم بتحويل باقي النص الى تعليق ، وبالتالي يقوم الاستعلام بجلب اول مستخدم في جدول المستخدمين وتسجيل الدخول على انه هو
تسجيل الدخول كأدمن :
ليس هذا اسوأ كوابيسك بعد ،
يمكن ايضا تسخيل الدخول بصلاحيات الادمن ، شغل البرنامج وادخل التالي :
الان نحن مسجلين كأدمن ، ما حدث هو ان نص الاستعلام اصبح :
نسجل باسم الادم ونلغي الجزء الخاص بالتحقق من كلمة السر عن طريق --
مسح جدول كامل :
اذا كنت تظن ان ما سبق هو اسوأ كوابيسك ، فانت مخطأ للغاية
العلامة ; تستخدم لانهاء سطر الاستعلام الحالي و بدأ سطر استعلام جديد ، مثل استعلام Drop فعلا لحذف جدول
شغل البرنامج وادخل في مربع اسم المستخدم :
اضغط تسجيل دخول ،
لم يتم تسجيل الدخول ولكن ليس هذا ما نبحث عنه
اذهب لقائمة الجداول تجد جدول Accounts اختفى !
طيب ما الحل ؟
استخدام ال Parameters :
الفكرة هنا هو حل المشكلة حلا جذريا ، وهو عن طريق عدم تمرير القيم وسط الاستعلام وانما تعريف بارمترات @ ومن ثم اضافتها بالطريقة التالية :
الان سيتم معاملة النصوص المدخلة من المستخدم حرفيا ،
اذا ادخل المستخدم مثلا ' Or 1=1 -- فهذا يعني انه يقصد مستخدم اسمه ' Or 1=1 -- وبالتالي عندما نبحث عنه لن نجد اي مستخدم بهذا الاسم ، فنصبح بامان من ال SQL INJECTION
الخلاصة :
يجب ان تعلم ان كل ما يتطلبه مهاجمة برنامجك والتحكم الكامل بقاعدة البيانات هو مربع نص تتكاسل عن استخدام Parameters معه ، فيصبح نافذة المستخدم الى قاعدة البيانات
ملاحظات :
يوجد طرق اضافية تقيك من هجمات ال SQL INJECTION التقليدية مثل :
1. استخدام ORM : Object-relational Mapper مثل Entity Framework او Linq to Sql او Dapper ، لانك لاتمرر قيم لاستعلامات اصلا بل تعتمد على الكائنات
2. تطهير المدخلات Input sanitization : يعتمد هذا الاسلوب على تنقية مدخلات المستخدم من الرموز التي يمكن ان تغير وظيفة الاستعلام
مراجع :
مرجع لاساليب حقت الاستعلامات SQL INJECTION :
https://www.netsparker.com/blog/web-secu...eat-sheet/
هذا الموقع يشرح ال SQL INJECTION ويمكنك تجريبه عليه :
https://www.hacksplaining.com/exercises/sql-injection
هذا الموقع ايضا ليس محمي يمكنك تجريب تسجيل الدخول عليه :
https://demo.testfire.net/
تم اضافة ملف قاعدة البيانات
قم بعمل Attach لتجربة البرنامج ، او غير نص الاتصال
السلام عليكم ورحمة الله وبركاتة
لتذكير نفسي واياكم اكتب هذا الموضوع لتوضيح نقطة هامة جدا تتعلق ببناء الاستعلامات الديناميكية داخل البرنامج ،لكي يكون الكل على دراية بها
تقديم :
حينما تختار ان تربط برنامجك بقاعدة بيانات يجب ان تعلم انك المسئول الوحيد عن امان هذه البيانات ، واعني بكلمة امان هنا الخصوصية ، ان لايطلع احد غير مصرح له على بيانات لا يجب ان يراها ، وكذلك اعني الاستمرارية ، ان لايحدث ان تفقد بياناتك او جزء منها
واحدة من اكبر نقط الضعف vulnerability عند بناء استعلامات في الكود هو تمرير بيانات من المستخدم مباشرة الى نص الاستعلام ، يجب ان تستحضر دائما ان نص الاستعلام ما هو الا نص String ، اذا اُدخل فيه نص استعلام اخر صالح سيقبله محرك قاعدة البيانات ايضا
حينما تضع مربع نص في البرنامج ، تأخذ منه المدخل ، وتضعه مباشرة في الاستعلام ، فانت فعليا فتحت نافذه من مربع النص هذا الى قاعدة البيانات ، اذا كان المستخدم جيدا بما فيه الكفاية يمكنه بخطوات غاية البساطة كما سأشرح في هذا الموضوع فعل كل شيء بقاعدة بياناتك ، بما يتضمنه سرقة البيانات او حتى ضياعها
كود :
Dim CommandString As String = "SELECT [Name] FROM [Users] WHERE [Name]='" & txtName.Text & "'"
اذا كنت لازلت تبني نص الاستعلام بهذه الطريقة فانت بحاجة الى ان تتوقف عن ذلك فورا ،
للتجربة ، سنحاكي نظام حسابات بسيط به فورم تسجيل دخول وفورم الحسابات وقاعدة بيانات بها جدول للمستخدمين وجدول للحسابات
انشأ مشروع جديد ، عدل فورم تسجيل الدخول الى الشكل التالي :
وفورم الحسابات :
جدول المستخدمين :
وضعنا به بيانات مستخدمين للتجربة :
جدول الحسابات :
تصميم برنامج ضعيف ضد SQL INJECTION :
حقن الاستعلامات او Sql Injection هي طريقة لمهاجمة قواعد البيانات عن طريق استغلال المدخلات الغير محمية ، بحيث تقوم بادخال في نص الاستعلام ما يقوم بتغير وظيفته او اضافة وظيفة جديده له تخدم المهاجم
في برنامجنا البسيط نريد ان نتأكد من ان المستخدم الذي يحاول تسجيل الدخول موجود فعلا في جدول المستخدمين ، وربما ايضا التأكد من صلاحياته
لفعل ذلك نقوم بعمل استعلام يعود باسم المستخدم في حال تطابق اسم المستخدم المدخل مع احد اعمدة جدول المستخدمين وكذلك كلمة السر المدخلة مع نفس العمود
(وجب ان انبه مرة اخرى ان هذه هي الطريقة الخاطئة ، للتجربة فقط)
يكون نص الاستعلام :
كود :
Dim CommandString As String = "SELECT [Name] FROM [Users] WHERE [Name]='" & txtName.Text & "' AND [Password]='" & txtPassword.Text & "'"
ثم نكمل باقي الكود لاتمام العملية ، ExecuteScalar تعود باول خلية في اول صف وهذا ما نريده :
كود :
Dim conn As New SqlConnection(ConnectionString)
Dim Command As New SqlCommand(CommandString, conn)
conn.Open()
Dim UserName As String = Command.ExecuteScalar()
conn.Close()
الان نختبر اذا كان UserName يحمل قيمة هذا معناه ان المستخدم موجود فعلا ، وبالتالي نفتح الفورم الرئيسي ونمرر له اسم المستخدم :
كود :
If UserName IsNot Nothing Then
Dim frm As New MainForm
frm.lblUserName.Text = UserName
frm.Show()
Close()
Else
MsgBox("اسم المستخدم او كلمة المرور غير صحيحة")
End If
كود بسيط جدا ،
الان شغل البرنامج واكتب اسم مستخدم خاطيء :
الان اكتب اسم مستخدم وكلمة سر من جدول المستخدمين فعلا :
ممتاز جدا الى الان لا مشاكل ولكن انتظر
مهاجمة البرنامج باستخدام SQL INJECTION:
تعطيل العمل:
طيب جرب افتح البرنامج من جديد وادخل الرمز ' فقط في اسم المستخدم واضغط تسجيل الدخول :
ينهار البرنامج ويعطيك الخطأ التالي :
إقتباس :Unclosed quotation mark after the character string '' AND [Password]=''.
Incorrect syntax near '' AND [Password]=''.
ما معنى ذلك ؟ يخبرك البرنامج ان هناك قوس نص ' غير مغلق ، ادركت حجم المصيبة ؟ قاعدة البيانات اعتبرت مدخل المستخدم جزء من الاستعلام وليس مجرد مدخل
تخطي التسجيل :
هل هذا اسوأ ما قد يحدث ؟
بالطبع لا ، من الممكن ببساطة في مثالنا ان نتخطى عملية التسجيل اصلا ،
افتح البرنامج من جديد و ادخل الجملة التالية في مربع اسم المستخدم ، وهي الاشهر فيما يتعلق بال SQL INJECTION :
PHP كود :
' OR 1=1 --
ثم اضغط تسجيل دخول
دخلنا فعلا دون اسم مستخدم او كلمة سر ! كيف حدث هذا
دعنا نراجع نص الاستعلام الذي استخدمناه في حال ادخال اسم المستخدم : user1 وكلمة السر : 12345678
PHP كود :
SELECT [Name] FROM [Users] WHERE [Name]='user1' AND [Password]='12345678'
طبيعي جدا ، طيب ماذا يحدث في حالة ادخال :
' OR 1=1 --
يصبح شكل نص الاستعلام كالتالي :
PHP كود :
SELECT [Name] FROM [Users] WHERE [Name]='' OR 1=1 -- ' AND [Password]=''
' نغلق بها قوس النصوص المفتوح و OR 1=1 نجعل بها الشرط متحقق دائما و -- تقوم بتحويل باقي النص الى تعليق ، وبالتالي يقوم الاستعلام بجلب اول مستخدم في جدول المستخدمين وتسجيل الدخول على انه هو
تسجيل الدخول كأدمن :
ليس هذا اسوأ كوابيسك بعد ،
يمكن ايضا تسخيل الدخول بصلاحيات الادمن ، شغل البرنامج وادخل التالي :
PHP كود :
admin' --
الان نحن مسجلين كأدمن ، ما حدث هو ان نص الاستعلام اصبح :
كود :
SELECT [Name] FROM [Users] WHERE [Name]='admin' --' AND [Password]=''
نسجل باسم الادم ونلغي الجزء الخاص بالتحقق من كلمة السر عن طريق --
مسح جدول كامل :
اذا كنت تظن ان ما سبق هو اسوأ كوابيسك ، فانت مخطأ للغاية
العلامة ; تستخدم لانهاء سطر الاستعلام الحالي و بدأ سطر استعلام جديد ، مثل استعلام Drop فعلا لحذف جدول
شغل البرنامج وادخل في مربع اسم المستخدم :
PHP كود :
' ; DROP TABLE Accounts ; --
اضغط تسجيل دخول ،
لم يتم تسجيل الدخول ولكن ليس هذا ما نبحث عنه
اذهب لقائمة الجداول تجد جدول Accounts اختفى !
طيب ما الحل ؟
استخدام ال Parameters :
الفكرة هنا هو حل المشكلة حلا جذريا ، وهو عن طريق عدم تمرير القيم وسط الاستعلام وانما تعريف بارمترات @ ومن ثم اضافتها بالطريقة التالية :
كود :
Dim CommandString As String = "SELECT [Name] FROM [Users] WHERE [Name]=@Name AND [Password]=@Password"
Dim conn As New SqlConnection(ConnectionString)
Dim Command As New SqlCommand(CommandString, conn)
Command.Parameters.AddWithValue("@Name", txtName.Text)
Command.Parameters.AddWithValue("@Password", txtPassword.Text)
conn.Open()
Dim UserName As String = Command.ExecuteScalar()
conn.Close()
If UserName IsNot Nothing Then
Dim frm As New MainForm
frm.Show()
frm.lblUserName.Text = UserName
Close()
Else
MsgBox("اسم المستخدم او كلمة المرور غير صحيحة")
End If
الان سيتم معاملة النصوص المدخلة من المستخدم حرفيا ،
اذا ادخل المستخدم مثلا ' Or 1=1 -- فهذا يعني انه يقصد مستخدم اسمه ' Or 1=1 -- وبالتالي عندما نبحث عنه لن نجد اي مستخدم بهذا الاسم ، فنصبح بامان من ال SQL INJECTION
الخلاصة :
يجب ان تعلم ان كل ما يتطلبه مهاجمة برنامجك والتحكم الكامل بقاعدة البيانات هو مربع نص تتكاسل عن استخدام Parameters معه ، فيصبح نافذة المستخدم الى قاعدة البيانات
ملاحظات :
يوجد طرق اضافية تقيك من هجمات ال SQL INJECTION التقليدية مثل :
1. استخدام ORM : Object-relational Mapper مثل Entity Framework او Linq to Sql او Dapper ، لانك لاتمرر قيم لاستعلامات اصلا بل تعتمد على الكائنات
2. تطهير المدخلات Input sanitization : يعتمد هذا الاسلوب على تنقية مدخلات المستخدم من الرموز التي يمكن ان تغير وظيفة الاستعلام
مراجع :
مرجع لاساليب حقت الاستعلامات SQL INJECTION :
https://www.netsparker.com/blog/web-secu...eat-sheet/
هذا الموقع يشرح ال SQL INJECTION ويمكنك تجريبه عليه :
https://www.hacksplaining.com/exercises/sql-injection
هذا الموقع ايضا ليس محمي يمكنك تجريب تسجيل الدخول عليه :
https://demo.testfire.net/
تم اضافة ملف قاعدة البيانات
قم بعمل Attach لتجربة البرنامج ، او غير نص الاتصال