حقن SQL هو طريقة هجوم بسيطة للغاية ، لكنها لا تزال شائعة جدًا حتى يومنا هذا. السبب ليس أكثر من: لا تصحيح للغبي. لماذا تقول ذلك؟ لنأخذ جافا كمثال لتوضيح:
افترض أن هناك جدولًا كهذا في قاعدة البيانات:
مستخدم الجدول (ID varchar (20) المفتاح الأساسي ، الاسم Varchar (20) ، Age Varchar (20)) ؛
ثم استخدم JDBC لتشغيل الجدول:
السلسلة الخاصة getNamebyUSerId (سلسلة userId) {connection conn = getConn () ؛ // الحصول على سلسلة الاتصال sql = "حدد الاسم من المستخدم حيث id =" + userId ؛ reparedStatement PSTMT = conn.preparestatement (SQL) ؛ resultset rs = pstmt.executeupdate () ؛ ......}غالبًا ما يتم استخدام الكود أعلاه من قبل بعض المطورين. تخيل هذا الموقف ، عندما تكون معلمة UserDed التي تم تمريرها هي "3 ؛ مستخدم جدول إسقاط ؛" ، فإن عبارة SQL المنفذة هي كما يلي:
حدد الاسم من المستخدم حيث معرف = 3 ؛ إسقاط الجدول مستخدم ؛
بعد تجميع قاعدة البيانات وتنفيذها ، يتم حذف جدول المستخدم. انظر ، هجوم حقن SQL بسيط هو في الواقع! وذلك لأن الكود أعلاه لا يتوافق مع مواصفات البرمجة.
حقن SQL غير موجود عندما نبرمج وفقًا للمواصفات. هذه هي الطريقة الأولى لتجنب حقن SQL: عبارات مسبقة ، الرمز كما يلي:
connection conn = getConn () ؛ // get connection string sql = "حدد الاسم من المستخدم حيث id =؟" ؛ reparedStatement PSTMT = conn.preparestatement (SQL) ؛ pStMt.SetString (1 ، userId) ؛ resultset rs = pstmt.executeupdate () ؛ ....
لماذا لا يوجد حقن SQL في الكود أعلاه؟ نظرًا لاستخدام العبارات المسبقة ، فإن العبارة المسبقة ستجمع "تحديد اسم من المستخدم حيث معرف =؟" بيان مقدمًا عند التنفيذ ، لذلك عند التنفيذ ، تحتاج فقط إلى استبداله بالمعلمات التي تم تمريرها؟ عنصر نائب. بالنسبة للحالة الأولى التي لا تتوافق مع المواصفات ، سيقوم البرنامج بإنشاء عبارات SQL ثم تجميعها بالمحتوى الذي يمرره المستخدم. هذه هي بالضبط المشكلة.
بالإضافة إلى استخدام البيانات المسبقة ، هناك طريقة ثانية لتجنب هجمات حقن SQL: الإجراءات المخزنة. الإجراء المخزن هو مجموعة من عبارات SQL التي تكمل وظائف محددة. بعد التجميع ، يتم تخزينه في قاعدة البيانات. يمكن للمستخدمين تنفيذها عن طريق استدعاء الإجراءات المخزنة وإعطاء المعلمات (إذا كان للإجراء المخزن معلمات) ، ويمكنهم أيضًا تجنب هجمات حقن SQL.
Connection conn = getConn () ؛ stmt = conn.preparecall ("{call name_from_user (؟ ،؟)}") ؛ stmt.setint (1،2) ؛ stmt.registerOutParameter (2 ، types.varchar) ؛ stmt.execute () ؛ اسم السلسلة = stmt.getString (2) ؛الإجراءات المخزنة المقابلة في الكود أعلاه هي كما يلي:
استخدم المستخدم ؛ SELIMITER // إنشاء الإجراء NAME_FROM_USER (في user_id int ، out user_name varchar (20)) ابدأ تحديد الاسم في user_name من المستخدم حيث user_id ؛ نهاية // محدد ؛
بالطبع ، يمكن للمستخدمين أيضًا القيام بفحص الأحرف على الواجهة الأمامية ، والتي تعد أيضًا وسيلة لتجنب حقن SQL: على سبيل المثال ، بالنسبة لمعلمة userId المستخدم أعلاه ، سيطالب المستخدم بخطأ عند التحقق من تضمين Semicolon.
ومع ذلك ، من الأسباب الأساسية ، يوجد هجوم حقن SQL لأن التطبيق لا يستخدم الحد الأدنى من الأذونات عند الوصول إلى قاعدة البيانات. أعتقد ذلك ، يبدو أن كل شخص يستخدم حساب الجذر للوصول إلى قاعدة البيانات.
فكيف يتجنب MyBatis هجمات حقن SQL؟ دعنا نستخدم مستخدم الجدول أعلاه كمثال:
افترض أن ملف Mapper هو:
<حدد ID = "getNameByUserId" resultType = "String"> حدد الاسم من المستخدم حيث id = #{userId} </revelop>ملف Java المقابل هو:
الواجهة العامة usermapper {String getNameByUserId (param ("userId") userId userD) ؛ }يمكنك أن ترى أن معلمة الإدخال هي مستخدم من نوع السلسلة. عندما نقوم بتمرير userId = "34 ؛ مستخدم جدول إسقاط ؛" البيان المطبوع كما يلي:
حدد الاسم من المستخدم أين المعرف =؟
بغض النظر عن إدخال userId ، فإن عبارات SQL له مثل هذا. ويرجع ذلك إلى استخدام البيانات المسبقة في التنفيذ الأساسي. عندما تنفذ قاعدة البيانات هذا البيان ، فإنها تستخدم مباشرة العبارة المسبقة ثم تحل محل العنصر النائب مع userId الذي تم تمريره؟ فقط اذهب إلى الجري. غير موجود لاستبدال العنصر النائب أولاً؟ يتم تنفيذ عملية التجميع ، لذلك لا يوجد مجال للبقاء على قيد الحياة لحقن SQL.
فكيف يحقق MyBatis مسبقًا من SQL؟ في الواقع ، فإن الإطار يستخدم فئة المعدة. لا يتجنب فئة الجهة المعدلة حقن SQL فحسب ، لأنه تم تجميعه. عندما يتم تنفيذ نفس بيان SQL N Times ، فإنه يحفظ وقت التجميع (N-1) ، وبالتالي تحسين الكفاءة.
إذا قمت بتغيير البيان أعلاه إلى:
<حدد ID = "getNameByUserId" resultType = "String"> حدد الاسم من المستخدم حيث id = $ {userId} </select> عندما ندخل userId="34;drop table user;" ، البيان المطبوع يبدو هكذا:
حدد الاسم من المستخدم حيث معرف = 34 ؛ Drop Table User ؛
في هذا الوقت ، لا يستخدم MyBatis عبارات مسبقة. سيقوم أولاً بإجراء خياطة السلسلة ثم إجراء التجميع. هذه العملية هي عملية حقن SQL.
لذلك ، عند كتابة عبارات تعيين MyBatis ، حاول استخدام التنسيق "#{xxx}". إذا كان عليك استخدام معلمات مثل "$ {xxx}" ، فيجب عليك القيام بعمل جيد في التصفية يدويًا لمنع هجمات حقن SQL.
لخص
ما سبق هو شرح مفصل لطريقة منع حقن SQL بواسطة MyBatis الذي قدمه لك. آمل أن يكون ذلك مفيدًا لك. إذا كان لديك أي أسئلة ، فيرجى ترك رسالة لي وسوف يرد المحرر إليك في الوقت المناسب. شكرا جزيلا لدعمكم لموقع wulin.com!