การฉีด SQL เป็นวิธีการโจมตีที่ง่ายมาก แต่ก็ยังเป็นเรื่องธรรมดามากในทุกวันนี้ เหตุผลไม่มีอะไรมากไปกว่า: ไม่มีแพทช์สำหรับโง่ ทำไมคุณถึงพูดแบบนั้น? ลองใช้ Java เป็นตัวอย่างเพื่ออธิบาย:
สมมติว่ามีตารางเช่นนี้ในฐานข้อมูล:
ผู้ใช้ตาราง (ID VARCHAR (20) คีย์หลักชื่อ VARCHAR (20), อายุ VARCHAR (20));
จากนั้นใช้ JDBC เพื่อใช้งานตาราง:
สตริงส่วนตัว getNameByUserId (String userId) {การเชื่อมต่อ conn = getConn (); // รับสตริงการเชื่อมต่อ sql = "เลือกชื่อจากผู้ใช้ที่ id =" + userId; PreparedStatement PSTMT = Conn.PrepareStatement (SQL); resultset rs = pstmt.executeUpdate (); -โค้ดด้านบนมักใช้โดยนักพัฒนาบางคน ลองนึกภาพสถานการณ์นี้เมื่อพารามิเตอร์ผู้ใช้ที่ส่งผ่านคือ "3; ผู้ใช้ Drop Table;", คำสั่ง SQL ที่ดำเนินการมีดังนี้:
เลือกชื่อจากผู้ใช้ที่ ID = 3; ผู้ใช้ Table Drop;
หลังจากรวบรวมและดำเนินการฐานข้อมูลตารางผู้ใช้จะถูกลบ ดูการโจมตีแบบฉีด SQL อย่างง่ายมีผล! นี่เป็นเพราะรหัสข้างต้นไม่สอดคล้องกับข้อกำหนดการเขียนโปรแกรม
การฉีด SQL ไม่มีอยู่เมื่อเราตั้งโปรแกรมตามข้อกำหนด นี่เป็นวิธีแรกในการหลีกเลี่ยงการฉีด SQL: คำสั่งที่คอมไพล์ล่วงหน้ารหัสมีดังนี้:
การเชื่อมต่อ conn = getConn (); // รับสตริงการเชื่อมต่อ sql = "เลือกชื่อจากผู้ใช้ที่ id =?"; PreparedStatement PSTMT = Conn.PrepareStatement (SQL); pstmt.setstring (1, userid); resultset rs = pstmt.executeUpdate (); -
เหตุใดการฉีด SQL จึงไม่มีอยู่ในรหัสข้างต้น? เนื่องจากมีการใช้คำสั่ง precompiled คำสั่ง precompiled จะรวบรวม "เลือกชื่อจากผู้ใช้ที่ id =?" คำสั่งล่วงหน้าเมื่อดำเนินการดังนั้นเมื่อดำเนินการคุณจะต้องแทนที่ด้วยพารามิเตอร์ที่ผ่านเท่านั้น? ตัวยึดตำแหน่ง สำหรับกรณีแรกที่ไม่สอดคล้องกับข้อกำหนดโปรแกรมจะสร้างคำสั่ง SQL จากนั้นรวบรวมพวกเขาด้วยเนื้อหาที่ส่งโดยผู้ใช้ นี่เป็นปัญหาที่แน่นอน
นอกเหนือจากการใช้คำสั่ง precompiled แล้วยังมีวิธีที่สองในการหลีกเลี่ยงการโจมตีการฉีด 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);ขั้นตอนที่เก็บไว้ในรหัสข้างต้นมีดังนี้:
ใช้ผู้ใช้; Delimiter // สร้างโพรซีเดอร์ name_from_user (ใน user_id int, out user_name varchar (20)) เริ่มเลือกชื่อเป็น user_name จากผู้ใช้ที่ id = user_id; สิ้นสุด // คั่น;
แน่นอนว่าผู้ใช้ยังสามารถตรวจสอบอักขระได้ที่ส่วนหน้าซึ่งเป็นวิธีที่จะหลีกเลี่ยงการฉีด SQL: ตัวอย่างเช่นสำหรับพารามิเตอร์ผู้ใช้ด้านบนผู้ใช้จะแจ้งข้อผิดพลาดเมื่อตรวจสอบว่ามีเซมิโคลอนรวมอยู่ด้วย
อย่างไรก็ตามจากเหตุผลพื้นฐานที่สุดการโจมตีการฉีด SQL มีอยู่เนื่องจากแอพไม่ได้ใช้สิทธิ์ขั้นต่ำเมื่อเข้าถึงฐานข้อมูล ฉันคิดอย่างนั้นดูเหมือนว่าทุกคนใช้บัญชีรูทเพื่อเข้าถึงฐานข้อมูล
ดังนั้น mybatis หลีกเลี่ยงการโจมตี SQL ได้อย่างไร? มาใช้ผู้ใช้ตารางด้านบนเป็นตัวอย่าง:
สมมติว่าไฟล์ mapper คือ:
<select id = "getNameByUserId" resultType = "String"> เลือกชื่อจากผู้ใช้โดยที่ id = #{userId} </select>ไฟล์ Java ที่เกี่ยวข้องคือ:
อินเทอร์เฟซสาธารณะ usermapper {สตริง getNameByUserId (@param ("userId") userId); -คุณจะเห็นว่าพารามิเตอร์อินพุตเป็นผู้ใช้ประเภทสตริง เมื่อเราผ่าน userId = "34; ผู้ใช้ Drop Table;" คำสั่งที่พิมพ์มีดังนี้:
เลือกชื่อจากผู้ใช้ที่ id =?
ไม่ว่าผู้ใช้จะป้อนอะไรคำสั่ง SQL ของเขาก็เป็นเช่นนี้ นี่เป็นเพราะการใช้คำสั่ง precompiled ในการดำเนินการพื้นฐาน เมื่อฐานข้อมูลดำเนินการคำสั่งนี้มันจะใช้คำสั่ง precompiled โดยตรงจากนั้นแทนที่ตัวยึดตำแหน่งด้วยผู้ใช้ที่ผ่านมาหรือไม่ แค่ไปวิ่ง ไม่มีอยู่เพื่อแทนที่ตัวยึดตำแหน่งก่อน? กระบวนการรวบรวมดำเนินการดังนั้นจึงไม่มีที่ว่างสำหรับการอยู่รอดสำหรับการฉีด SQL
ดังนั้น mybatis จะบรรลุการคอมไพล์ SQL ล่วงหน้าได้อย่างไร? ในความเป็นจริงเฟรมเวิร์กกำลังใช้คลาส PreparedStatement คลาส PreparedStaement ไม่เพียง แต่หลีกเลี่ยงการฉีด SQL เนื่องจากได้รับการคอมไพล์ล่วงหน้า เมื่อคำสั่ง SQL เดียวกันถูกดำเนินการ N ครั้งจะช่วยประหยัดเวลาในการรวบรวม (N-1) ซึ่งจะช่วยปรับปรุงประสิทธิภาพ
หากคุณเปลี่ยนคำสั่งข้างต้นเป็น:
<select id = "getNameByUserId" resultType = "String"> เลือกชื่อจากผู้ใช้ที่ id = $ {userId} </select> เมื่อเราป้อน userId="34;drop table user;" คำสั่งที่พิมพ์มีลักษณะเช่นนี้:
เลือกชื่อจากผู้ใช้ที่ id = 34; ผู้ใช้ตารางวาง;
ในเวลานี้ MyBatis ไม่ได้ใช้คำสั่งที่รวบรวมไว้ล่วงหน้า มันจะทำการเย็บสตริงก่อนแล้วทำการรวบรวม กระบวนการนี้เป็นกระบวนการของการฉีด SQL ที่มีผล
ดังนั้นเมื่อเขียนคำสั่ง MyBatis Mapping ให้ลองใช้รูปแบบ "#{xxx}" หากคุณต้องใช้พารามิเตอร์เช่น "$ {xxx}" คุณต้องทำงานกรองได้ดีด้วยตนเองเพื่อป้องกันการโจมตีการฉีด SQL
สรุป
ข้างต้นเป็นคำอธิบายโดยละเอียดเกี่ยวกับวิธีการป้องกันการฉีด SQL โดย mybatis แนะนำให้คุณ ฉันหวังว่ามันจะเป็นประโยชน์กับคุณ หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉันและบรรณาธิการจะตอบกลับคุณทันเวลา ขอบคุณมากสำหรับการสนับสนุนเว็บไซต์ Wulin.com!