รหัสนี้ทำงานอย่างถูกต้องเพราะเป็น:
- พารามิเตอร์และ
- ไม่ทำ Dynamic SQL ใด ๆ
เพื่อให้ SQL Injection ทำงานได้คุณจะต้องสร้างสตริงการสืบค้น (ซึ่งคุณไม่ได้ทำ) และไม่ต้องแปลเครื่องหมายอัญประกาศเดี่ยว ('
) ไปเป็น escaped-apostrophes ( ''
) (เหล่านั้นถูกหลบหนีผ่านพารามิเตอร์อินพุต)
ในความพยายามของคุณที่จะส่งผ่านค่า "ทำลาย" 'Male; DROP TABLE tblActor'
สตริงเป็นเพียงสตริงธรรมดา ol '
ตอนนี้ถ้าคุณกำลังทำบางสิ่งตามแนวของ:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = '
+ @InputParam;
EXEC(@SQL);
แล้วว่าจะเป็นความเสี่ยงที่จะ SQL Injection เพราะที่แบบสอบถามไม่ได้อยู่ในปัจจุบันบริบทก่อนแจง; การค้นหานั้นเป็นเพียงสายอักขระอื่นในขณะนี้ ดังนั้นค่าของ@InputParam
อาจเป็น'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
และที่อาจมีปัญหาเนื่องจากแบบสอบถามนั้นจะแสดงผลและดำเนินการตาม:
SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
นี่คือเหตุผลหลักหนึ่งข้อ (ในหลาย ๆ ข้อ) ในการใช้ Stored Procedure: ปลอดภัยยิ่งขึ้น (ตราบใดที่คุณไม่หลีกเลี่ยงการรักษาความปลอดภัยโดยการสร้างเคียวรีอย่างที่ฉันแสดงข้างต้นโดยไม่ต้องตรวจสอบค่าของพารามิเตอร์ที่ใช้) แม้ว่าคุณจะต้องสร้าง Dynamic SQL แต่วิธีที่ต้องการก็คือการกำหนดพารามิเตอร์ด้วยเช่นกันโดยใช้sp_executesql
:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';
EXEC sp_executesql
@SQL,
N'SomeDate_tmp DATETIME',
@SomeDate_tmp = @InputParam;
โดยใช้วิธีการนี้มีคนพยายามที่จะผ่านใน'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
สำหรับDATETIME
พารามิเตอร์สำหรับการป้อนจะได้รับข้อผิดพลาดเมื่อมีการดำเนินขั้นตอนการเก็บ หรือแม้กระทั่งถ้าขั้นตอนการเก็บได้รับการยอมรับ@InputParameter
เป็นNVARCHAR(100)
ก็จะมีการแปลงไปDATETIME
เพื่อที่จะผ่านเข้ามาที่sp_executesql
โทร และแม้ว่าพารามิเตอร์ใน Dynamic SQL เป็นประเภทสตริงการเข้าสู่กระบวนงานที่เก็บไว้ในสถานที่แรกที่เครื่องหมายอัญประกาศเดี่ยวใด ๆ จะได้รับการหลบหนีโดยอัตโนมัติไปยังเครื่องหมายวรรคตอนคู่
มีประเภทของการโจมตีที่รู้จักกันน้อยกว่าซึ่งผู้โจมตีพยายามกรอกข้อมูลในฟิลด์อินพุตด้วยเครื่องหมายอัญประกาศเดี่ยวซึ่งสตริงภายในกระบวนงานที่เก็บไว้ซึ่งจะใช้ในการสร้าง Dynamic SQL แต่มีการประกาศว่ามีขนาดเล็กเกินไปไม่สามารถพอดีกับทุกสิ่งได้ และผลักเครื่องหมายอะพอสโทรฟีตอนจบออกมาและจบลงด้วยจำนวนอะพอสโทรฟีที่ถูกต้องเพื่อไม่ให้ "หลบหนี" ภายในสตริงอีกต่อไป สิ่งนี้เรียกว่าการตัดทอนของ SQL และถูกพูดถึงในบทความในนิตยสาร MSDN เรื่อง "การโจมตีการตัดทอนของ SQL ใหม่และวิธีหลีกเลี่ยงพวกเขา" โดย Bala Neerumalla แต่บทความนี้ไม่ได้ออนไลน์อีกต่อไป ปัญหาที่มีบทความนี้ - นิตยสาร MSDN ฉบับเดือนพฤศจิกายน 2549 - มีให้ในรูปแบบไฟล์วิธีใช้ของ Windows เท่านั้น (ใน . chmรูปแบบ). หากคุณดาวน์โหลดอาจไม่สามารถเปิดได้เนื่องจากการตั้งค่าความปลอดภัยเริ่มต้น หากสิ่งนี้เกิดขึ้นให้คลิกขวาที่ไฟล์MSDNMagazineNovember2006en-us.chmและเลือก "Properties" ในหนึ่งในแท็บเหล่านั้นจะมีตัวเลือกสำหรับ "เชื่อถือไฟล์ประเภทนี้" (หรืออะไรทำนองนั้น) ที่จะต้องตรวจสอบ / เปิดใช้งาน คลิกปุ่ม "ตกลง" จากนั้นลองเปิดไฟล์. chmอีกครั้ง
อีกรูปแบบหนึ่งของการโจมตี Truncation คือสมมติว่ามีการใช้ตัวแปรท้องถิ่นเพื่อจัดเก็บค่าที่ผู้ใช้ระบุ "ปลอดภัย" เนื่องจากมีการอ้างอิงราคาเดียวสองเท่าเพื่อให้สามารถหลบหนีได้เพื่อเติมตัวแปรท้องถิ่นนั้นและวางเครื่องหมายคำพูดเดี่ยว ในตอนท้าย แนวคิดในที่นี้คือหากตัวแปรท้องถิ่นมีขนาดไม่ถูกต้องจะไม่มีที่ว่างพอสำหรับการอ้างคำพูดเดี่ยวครั้งที่สองให้ปล่อยตัวแปรที่ลงท้ายด้วยเครื่องหมายคำพูดเดี่ยวเดี่ยวที่รวมเข้ากับคำพูดเดียวที่ สิ้นสุดค่าตามตัวอักษรใน Dynamic SQL โดยเปลี่ยนเป็นสิ้นสุดเครื่องหมายคำพูดเดี่ยวเป็นเครื่องหมายอัญประกาศเพื่อหลีกเลี่ยงการฝังตัวและสตริงตัวอักษรใน Dynamic SQL จากนั้นลงท้ายด้วยเครื่องหมายอัญประกาศเดี่ยวถัดไปที่มีจุดประสงค์เพื่อเริ่มต้นตัวอักษรสตริงถัดไป ตัวอย่างเช่น:
-- Parameters:
DECLARE @UserID INT = 37,
@NewPassword NVARCHAR(15) = N'Any Value ....''',
@OldPassword NVARCHAR(15) = N';Injected SQL--';
-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
@NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
@OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');
SELECT @NewPassword AS [@NewPassword],
REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
@NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword REPLACE output @NewPassword_fixed
Any Value ....' Any Value ....'' Any Value ....'
*/
SELECT @OldPassword AS [@OldPassword],
REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
@OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword REPLACE output @OldPassword_fixed
;Injected SQL-- ;Injected SQL-- ;Injected SQL--
*/
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ @NewPassword_fixed + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ @OldPassword_fixed + N''';';
SELECT @SQL AS [Injected];
ที่นี่ Dynamic SQL ที่จะดำเนินการอยู่ในขณะนี้:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Dynamic SQL เดียวกันนั้นในรูปแบบที่อ่านได้มากขึ้นคือ:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';
Injected SQL--';
แก้ไขสิ่งนี้ได้อย่างง่ายดาย ทำอย่างใดอย่างหนึ่งต่อไปนี้:
- ไม่ได้ใช้ SQL แบบไดนามิกเว้นแต่อย่างจำเป็น! (ฉันแสดงรายการนี้ก่อนเพราะมันควรจะเป็นสิ่งแรกที่ต้องพิจารณา)
- ปรับขนาดตัวแปรโลคัลอย่างถูกต้อง (เช่นควรมีขนาดเป็นสองเท่าของพารามิเตอร์อินพุตในกรณีที่อักขระทุกตัวผ่านเป็นเครื่องหมายคำพูดเดี่ยว
อย่าใช้ตัวแปรท้องถิ่นเพื่อเก็บค่า "คงที่"; เพียงนำสิ่งREPLACE()
ต่อไปนี้เข้าสู่การสร้าง Dynamic SQL:
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ REPLACE(@OldPassword, N'''', N'''''') + N''';';
SELECT @SQL AS [No SQL Injection here];
Dynamic SQL ไม่ถูกบุกรุกอีกต่อไป:
UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
หมายเหตุเกี่ยวกับตัวอย่าง Trunction ด้านบน:
- ใช่นี่เป็นตัวอย่างที่ได้วางแผนไว้อย่างมาก มีไม่มากที่สามารถทำได้เพียง 15 ตัวอักษรในการฉีด แน่นอนว่าอาจเป็นการ
DELETE tableName
ทำลาย แต่มีโอกาสน้อยที่จะเพิ่มผู้ใช้หลังประตูหรือเปลี่ยนรหัสผ่านของผู้ดูแลระบบ
- การโจมตีประเภทนี้อาจเป็นไปได้ต้องใช้ความรู้เกี่ยวกับรหัสชื่อตาราง ฯลฯ มีโอกาสน้อยที่จะทำโดยคนแปลกหน้าแบบสุ่ม / script-kiddie แต่ฉันทำงานในสถานที่ที่ถูกโจมตีโดยอดีตพนักงานที่ค่อนข้างอารมณ์เสียซึ่งรู้จักช่องโหว่ ในหน้าเว็บหนึ่งที่ไม่มีใครรู้ ความหมายบางครั้งผู้โจมตีอาจมีความรู้อย่างลึกซึ้งเกี่ยวกับระบบ
- แน่นอนว่าการรีเซ็ตรหัสผ่านของทุกคนน่าจะถูกสอบสวนซึ่งอาจทำให้ บริษัท ไม่สามารถโจมตีได้ แต่อาจให้เวลาเพียงพอในการฉีดผู้ใช้แบ็คประตูหรืออาจได้รับข้อมูลรองเพื่อใช้ / หาประโยชน์ในภายหลัง
- แม้ว่าสถานการณ์นี้ส่วนใหญ่เป็นการศึกษา (เช่นไม่น่าจะเกิดขึ้นในโลกแห่งความเป็นจริง) ก็ยังคงเป็นไปไม่ได้
สำหรับข้อมูลโดยละเอียดเพิ่มเติมที่เกี่ยวข้องกับ SQL Injection (ครอบคลุม RDBMS และสถานการณ์ต่าง ๆ ) โปรดดูข้อมูลต่อไปนี้จากโครงการความปลอดภัยของโปรแกรมประยุกต์บนเว็บเปิด (OWASP):
การทดสอบสำหรับ SQL Injection
คำตอบ Stack Overflow ที่เกี่ยวข้องกับ SQL Injection และ SQL Truncation:
T-SQL ปลอดภัยแค่ไหนหลังจากที่คุณแทนที่ 'escape character?
EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'