Tại sao SQL Injection không xảy ra trên truy vấn này trong một thủ tục được lưu trữ?


18

Tôi đã thực hiện các thủ tục được lưu trữ sau đây:

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

Bây giờ, tôi đã thử làm một cái gì đó như thế này. Có thể tôi đang làm sai, nhưng tôi muốn chắc chắn rằng một quy trình như vậy có thể ngăn chặn bất kỳ SQL Injection nào:

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

Hình ảnh bên dưới cho thấy SQL ở trên được thực thi trong SSMS và kết quả được hiển thị chính xác thay vì lỗi:

nhập mô tả hình ảnh ở đây

Btw, tôi đã thêm phần đó sau dấu chấm phẩy sau khi truy vấn được thực hiện xong. Sau đó, tôi thực hiện lại, nhưng khi tôi kiểm tra xem bảng tblActor có tồn tại hay không, nó vẫn ở đó. Tôi có làm điều gì sai? Hay đây thực sự là thuốc tiêm? Tôi đoán những gì tôi đang cố gắng hỏi ở đây cũng là một thủ tục được lưu trữ như thế này an toàn? Cảm ơn bạn.


Bạn đã thử cái này chưaEXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
MarmiK

Câu trả lời:


38

Mã này hoạt động đúng bởi vì nó là:

  1. Tham số hóa, và
  2. Không thực hiện bất kỳ SQL động nào

Để SQL Injection hoạt động, bạn phải xây dựng một chuỗi truy vấn (mà bạn không làm) và không dịch các dấu nháy đơn ( ') thành các dấu nháy đơn ( '') được thoát qua các tham số đầu vào).

Trong nỗ lực của bạn để vượt qua một giá trị "bị xâm phạm", 'Male; DROP TABLE tblActor'chuỗi chỉ là một chuỗi đơn giản.

Bây giờ, nếu bạn đang làm một cái gì đó dọc theo dòng:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = '
          + @InputParam;

EXEC(@SQL);

sau đó sẽ dễ bị SQL Injection vì truy vấn đó không nằm trong bối cảnh hiện tại, được phân tích cú pháp trước; truy vấn đó chỉ là một chuỗi khác tại thời điểm này. Vì vậy, giá trị của @InputParamcó thể '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;và điều đó có thể gây ra vấn đề vì truy vấn đó sẽ được hiển thị và được thực thi, như:

SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;

Đây là một (một vài) lý do chính để sử dụng Quy trình được lưu trữ: vốn đã an toàn hơn (miễn là bạn không phá vỡ sự bảo mật đó bằng cách xây dựng các truy vấn như tôi đã trình bày ở trên mà không xác thực các giá trị của bất kỳ tham số nào được sử dụng). Mặc dù nếu bạn cần xây dựng SQL động, cách ưa thích là sử dụng tham số đó bằng cách sử dụng 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;

Sử dụng phương pháp này, ai đó cố gắng để vượt qua trong '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;một DATETIMEtham số đầu vào sẽ nhận được một lỗi khi thực hiện các thủ tục lưu trữ. Hoặc thậm chí nếu các Stored Procedure chấp nhận @InputParameternhư NVARCHAR(100), nó sẽ phải chuyển đổi sang một DATETIMEđể thông qua vào mà sp_executesqlgọi. Và ngay cả khi tham số trong SQL động là loại chuỗi, đi vào Quy trình được lưu trữ ở vị trí đầu tiên, bất kỳ dấu nháy đơn nào cũng sẽ tự động thoát khỏi dấu nháy đơn.

Có một kiểu tấn công ít được biết đến hơn, trong đó kẻ tấn công cố gắng lấp đầy trường đầu vào bằng dấu nháy đơn để một chuỗi bên trong Quy trình được lưu trữ sẽ được sử dụng để xây dựng SQL động nhưng được khai báo quá nhỏ không thể phù hợp với mọi thứ và đẩy ra dấu nháy đơn kết thúc và bằng cách nào đó kết thúc với số dấu nháy đơn chính xác để không còn bị "thoát" trong chuỗi. Điều này được gọi là Cắt ngắn SQL và đã được nói đến trong một bài viết trên tạp chí MSDN có tiêu đề "Tấn công cắt ngắn SQL mới và cách tránh chúng", bởi Bala Neerumalla, nhưng bài viết không còn trực tuyến nữa. Vấn đề có chứa bài viết này - Tạp chí MSDN phiên bản tháng 11 năm 2006 - chỉ có sẵn dưới dạng tệp Trợ giúp của Windows (trong .chmđịnh dạng). Nếu bạn tải xuống, nó có thể không mở do cài đặt bảo mật mặc định. Nếu điều này xảy ra, sau đó nhấp chuột phải vào tệp MSDNMagazineNovember2006en-us.chm và chọn "Thuộc tính". Trong một trong các tab đó, sẽ có một tùy chọn cho "Tin tưởng loại tệp này" (hoặc một cái gì đó tương tự) cần được kiểm tra / kích hoạt. Nhấp vào nút "OK" và sau đó thử mở lại tệp .chm .

Một biến thể khác của tấn công Cắt ngắn là, giả sử một biến cục bộ được sử dụng để lưu trữ giá trị do người dùng cung cấp "an toàn" vì nó có bất kỳ dấu ngoặc đơn nào được nhân đôi để thoát ra, để điền vào biến cục bộ đó và đặt dấu ngoặc đơn cuối cùng. Ý tưởng ở đây là nếu biến cục bộ không có kích thước phù hợp, cuối cùng sẽ không có đủ chỗ cho trích dẫn đơn thứ hai, hãy để biến kết thúc bằng một trích dẫn đơn sau đó kết hợp với trích dẫn đơn kết thúc giá trị bằng chữ trong SQL động, biến đoạn kết thúc trích dẫn đó thành một trích dẫn thoát được nhúng và chuỗi ký tự trong SQL động sau đó kết thúc bằng trích dẫn đơn tiếp theo được dự định để bắt đầu chuỗi ký tự tiếp theo. Ví dụ:

-- 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];

Ở đây, SQL động sẽ được thực thi ngay bây giờ:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Cùng một SQL động, ở định dạng dễ đọc hơn, là:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';

Injected SQL--';

Sửa lỗi này là dễ dàng. Chỉ cần làm một trong những điều sau đây:

  1. KHÔNG SỬ DỤNG SQL NĂNG ĐỘNG KHÔNG GIỚI HẠN ! (Tôi liệt kê cái này trước vì nó thực sự nên là thứ đầu tiên cần xem xét).
  2. Kích thước chính xác biến cục bộ (nghĩa là phải gấp đôi kích thước làm tham số đầu vào, chỉ trong trường hợp tất cả các ký tự được truyền vào là dấu ngoặc đơn.
  3. Không sử dụng biến cục bộ để lưu trữ giá trị "cố định"; chỉ cần đặt REPLACE()trực tiếp vào việc tạo SQL động:

    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 không còn bị xâm phạm:

    UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Lưu ý về ví dụ Tractor ở trên:

  1. Vâng, đây là một ví dụ rất giả tạo. Không có nhiều thứ người ta có thể làm chỉ trong 15 ký tự. Chắc chắn, có thể DELETE tableNamelà phá hoại, nhưng ít có khả năng thêm người dùng cửa sau hoặc thay đổi mật khẩu quản trị viên.
  2. Kiểu tấn công này có thể đòi hỏi kiến ​​thức về mã, tên bảng, v.v ... Ít có khả năng được thực hiện bởi người lạ ngẫu nhiên / script-kiddie, nhưng tôi đã làm việc tại một nơi bị tấn công bởi một nhân viên cũ khá khó chịu, người biết về lỗ hổng trong một trang web cụ thể mà không ai khác biết. Có nghĩa là, đôi khi những kẻ tấn công có kiến ​​thức sâu sắc về hệ thống.
  3. Chắc chắn, việc thiết lập lại mật khẩu của mọi người có khả năng sẽ được điều tra, điều này có thể khiến công ty hiểu rằng có một cuộc tấn công đang xảy ra, nhưng nó vẫn có thể cung cấp đủ thời gian để tiêm cho người dùng cửa sau hoặc có thể lấy một số thông tin thứ cấp để sử dụng / khai thác sau này.
  4. Ngay cả khi kịch bản này chủ yếu là học thuật (nghĩa là không có khả năng xảy ra trong thế giới thực), nó vẫn không phải là không thể.

Để biết thêm thông tin chi tiết liên quan đến SQL Injection (bao gồm các kịch bản và kịch bản khác nhau của RDBMS), vui lòng xem phần sau đây từ Dự án bảo mật ứng dụng web mở (OWASP):
Kiểm tra SQL SQL

Câu trả lời tràn ngăn xếp liên quan về SQL Injection và SQL Truncation:
T-SQL an toàn đến mức nào sau khi bạn thay thế ký tự thoát?


2
Ồ, cảm ơn bạn rất nhiều, đây là một câu trả lời tuyệt vời. Giờ thì tôi đã hiểu. Tôi thực sự muốn thấy kỹ thuật mà bạn đã đề cập ở cuối, nơi kẻ tấn công cố gắng lấp đầy trường đầu vào bằng dấu nháy đơn, nếu bạn có thể tìm thấy nó. Cảm ơn trước. Tôi sẽ giữ điều này mở, trong trường hợp bạn không tìm thấy nó, tôi sẽ chọn đây là câu trả lời.
Ravi

1
@Ravi Tôi đã tìm thấy liên kết nhưng nó không còn được gửi đến bài viết vì tất cả chúng đều được lưu trữ. Nhưng tôi đã thêm một số thông tin và các liên kết hữu ích và tôi vẫn đang cố gắng tìm bài viết trong các tài liệu lưu trữ đó.
Solomon Rutzky

1
Cảm ơn bạn srutzsky, tôi sẽ đọc bài viết của OWASP và các xét nghiệm để tiêm. Nếu tôi nhớ chính xác, 'mutillidae', ứng dụng web dễ bị tấn công để kiểm tra bảo mật, đã tiêm SQL mà tôi đã thực hiện ở trường đại học với chuỗi 'OR 1 = 1' mà trong mutillidae dẫn đến việc tôi đăng nhập vào ứng dụng web với tư cách là quản trị viên suy nghĩ Đó là khi tôi lần đầu tiên được giới thiệu về SQL tiêm.
Ravi

1
Tôi cũng không thể xem tệp .chm, nhưng cảm ơn bạn vì câu trả lời hoàn hảo này và cho tất cả các liên kết hữu ích bao gồm liên kết từ stackoverflow và một từ OWASP. Tôi đọc nó ngày hôm nay và học được rất nhiều từ điều này.
Ravi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.