เหตุใด SQL Injection จึงไม่เกิดขึ้นกับแบบสอบถามนี้ภายในกระบวนงานที่เก็บไว้


18

ฉันทำขั้นตอนการจัดเก็บต่อไปนี้:

ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender

ตอนนี้ฉันพยายามทำอะไรแบบนี้ บางทีฉันอาจทำผิด แต่ฉันต้องการให้แน่ใจว่ากระบวนการดังกล่าวสามารถป้องกัน SQL Injection:

EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'

ภาพด้านล่างแสดง SQL ด้านบนที่ถูกดำเนินการใน SSMS และผลลัพธ์ถูกแสดงอย่างถูกต้องแทนที่จะมีข้อผิดพลาด:

ป้อนคำอธิบายรูปภาพที่นี่

Btw ฉันเพิ่มส่วนต่อไปที่อัฒภาคหลังจากแบบสอบถามเสร็จสิ้นการดำเนินการ จากนั้นฉันก็ดำเนินการอีกครั้ง แต่เมื่อฉันตรวจสอบเพื่อดูว่าตาราง tblActor มีอยู่หรือไม่มันยังคงอยู่ที่นั่น ฉันกำลังทำอะไรผิดหรือเปล่า? หรือนี่คือหลักฐานการฉีดจริง ๆ ? ฉันเดาว่าฉันพยายามถามอะไรที่นี่ยังเป็นขั้นตอนที่เก็บไว้เช่นนี้ปลอดภัยหรือไม่ ขอขอบคุณ.


คุณได้ลองสิ่งนี้แล้วหรือยังEXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
MarmiK

คำตอบ:


38

รหัสนี้ทำงานอย่างถูกต้องเพราะเป็น:

  1. พารามิเตอร์และ
  2. ไม่ทำ 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--';

แก้ไขสิ่งนี้ได้อย่างง่ายดาย ทำอย่างใดอย่างหนึ่งต่อไปนี้:

  1. ไม่ได้ใช้ SQL แบบไดนามิกเว้นแต่อย่างจำเป็น! (ฉันแสดงรายการนี้ก่อนเพราะมันควรจะเป็นสิ่งแรกที่ต้องพิจารณา)
  2. ปรับขนาดตัวแปรโลคัลอย่างถูกต้อง (เช่นควรมีขนาดเป็นสองเท่าของพารามิเตอร์อินพุตในกรณีที่อักขระทุกตัวผ่านเป็นเครื่องหมายคำพูดเดี่ยว
  3. อย่าใช้ตัวแปรท้องถิ่นเพื่อเก็บค่า "คงที่"; เพียงนำสิ่ง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 ด้านบน:

  1. ใช่นี่เป็นตัวอย่างที่ได้วางแผนไว้อย่างมาก มีไม่มากที่สามารถทำได้เพียง 15 ตัวอักษรในการฉีด แน่นอนว่าอาจเป็นการDELETE tableNameทำลาย แต่มีโอกาสน้อยที่จะเพิ่มผู้ใช้หลังประตูหรือเปลี่ยนรหัสผ่านของผู้ดูแลระบบ
  2. การโจมตีประเภทนี้อาจเป็นไปได้ต้องใช้ความรู้เกี่ยวกับรหัสชื่อตาราง ฯลฯ มีโอกาสน้อยที่จะทำโดยคนแปลกหน้าแบบสุ่ม / script-kiddie แต่ฉันทำงานในสถานที่ที่ถูกโจมตีโดยอดีตพนักงานที่ค่อนข้างอารมณ์เสียซึ่งรู้จักช่องโหว่ ในหน้าเว็บหนึ่งที่ไม่มีใครรู้ ความหมายบางครั้งผู้โจมตีอาจมีความรู้อย่างลึกซึ้งเกี่ยวกับระบบ
  3. แน่นอนว่าการรีเซ็ตรหัสผ่านของทุกคนน่าจะถูกสอบสวนซึ่งอาจทำให้ บริษัท ไม่สามารถโจมตีได้ แต่อาจให้เวลาเพียงพอในการฉีดผู้ใช้แบ็คประตูหรืออาจได้รับข้อมูลรองเพื่อใช้ / หาประโยชน์ในภายหลัง
  4. แม้ว่าสถานการณ์นี้ส่วนใหญ่เป็นการศึกษา (เช่นไม่น่าจะเกิดขึ้นในโลกแห่งความเป็นจริง) ก็ยังคงเป็นไปไม่ได้

สำหรับข้อมูลโดยละเอียดเพิ่มเติมที่เกี่ยวข้องกับ SQL Injection (ครอบคลุม RDBMS และสถานการณ์ต่าง ๆ ) โปรดดูข้อมูลต่อไปนี้จากโครงการความปลอดภัยของโปรแกรมประยุกต์บนเว็บเปิด (OWASP):
การทดสอบสำหรับ SQL Injection

คำตอบ Stack Overflow ที่เกี่ยวข้องกับ SQL Injection และ SQL Truncation:
T-SQL ปลอดภัยแค่ไหนหลังจากที่คุณแทนที่ 'escape character?


2
โอ้ขอบคุณมากนี่เป็นคำตอบที่ดีมาก ฉันเข้าใจแล้ว. ฉันอยากจะเห็นเทคนิคที่คุณพูดถึงในตอนท้ายด้วยโดยที่ผู้โจมตีพยายามเติมช่องใส่ข้อมูลด้วยเครื่องหมายอัญประกาศเดี่ยวหากคุณสามารถหาได้ ขอบคุณล่วงหน้า. ฉันจะเปิดเรื่องนี้ต่อไปในกรณีที่คุณไม่พบฉันจะเลือกคำตอบนี้
Ravi

1
@Ravi ฉันพบลิงค์ แต่ไม่ได้ไปที่บทความอีกต่อไปเนื่องจากตอนนี้พวกเขาทั้งหมดถูกเก็บถาวร แต่ฉันได้เพิ่มข้อมูลและลิงค์ที่มีประโยชน์บางส่วนและฉันยังคงพยายามค้นหาบทความภายในที่เก็บถาวรเหล่านั้น
โซโลมอน Rutzky

1
ขอบคุณ srutzsky ฉันจะอ่านบทความ OWASP และการทดสอบการฉีด ถ้าฉันจำได้อย่างถูกต้อง 'mutillidae' แอปพลิเคชันเว็บที่มีช่องโหว่สำหรับการทดสอบความปลอดภัยมีการฉีด SQL ที่ฉันได้ทำที่วิทยาลัยด้วยสตริง 'OR 1 = 1' ซึ่งใน mutillidae ทำให้ฉันลงชื่อเข้าใช้เว็บแอปในฐานะผู้ดูแลระบบฉัน คิด. นั่นคือตอนที่ฉันได้รู้จักกับการฉีด SQL ครั้งแรก
Ravi

1
ฉันไม่สามารถดูไฟล์. chm ได้เช่นกัน แต่ขอขอบคุณสำหรับคำตอบที่สมบูรณ์แบบนี้และสำหรับลิงก์ที่มีประโยชน์ทั้งหมดรวมถึงอันที่มาจาก stackoverflow และจาก OWASP ฉันอ่านวันนี้และเรียนรู้มากมายจากสิ่งนี้
Ravi

2

เรื่องง่าย ๆ คือคุณไม่สับสนข้อมูลกับคำสั่งเลย ค่าสำหรับพารามิเตอร์จะไม่ถือว่าเป็นส่วนหนึ่งของคำสั่งและจะไม่ถูกดำเนินการ

ฉันบล็อกเกี่ยวกับเรื่องนี้ที่: http://blogs.lobsterpot.com.au/2015/02/10/sql-inject-the-golden-rule/


ขอบคุณ Rob ฉันมีบุ๊คมาร์คเล่มนี้เพื่ออ่านในภายหลังในวันนั้น
Ravi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.