Tránh đưa vào SQL mà không có tham số


109

Chúng tôi đang có một cuộc thảo luận khác ở đây về việc sử dụng các truy vấn sql được tham số hóa trong mã của chúng tôi. Chúng tôi có hai mặt trong cuộc thảo luận: Tôi và một số người khác nói rằng chúng ta nên luôn sử dụng các thông số để bảo vệ chống lại việc tiêm sql và những người khác cho rằng nó không cần thiết. Thay vào đó, họ muốn thay thế các dấu nháy đơn bằng hai dấu nháy đơn trong tất cả các chuỗi để tránh tiêm sql. Cơ sở dữ liệu của chúng tôi đều đang chạy Sql Server 2005 hoặc 2008 và cơ sở mã của chúng tôi đang chạy trên .NET framework 2.0.

Hãy để tôi cung cấp cho bạn một ví dụ đơn giản trong C #:

Tôi muốn chúng tôi sử dụng cái này:

string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe

Trong khi những người khác muốn làm điều này:

string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?

Trong đó hàm SafeDBString được định nghĩa như sau:

string SafeDBString(string inputValue) 
{
    return "'" + inputValue.Replace("'", "''") + "'";
}

Bây giờ, miễn là chúng tôi sử dụng SafeDBString trên tất cả các giá trị chuỗi trong truy vấn của mình, chúng tôi sẽ an toàn. Đúng?

Có hai lý do để sử dụng chức năng SafeDBString. Đầu tiên, đó là cách nó đã được thực hiện từ thời kỳ đồ đá và thứ hai, việc gỡ lỗi các câu lệnh sql sẽ dễ dàng hơn vì bạn thấy truy vấn đào được chạy trên cơ sở dữ liệu.

Vậy thì. Câu hỏi của tôi là liệu nó có thực sự đủ để sử dụng hàm SafeDBString để tránh các cuộc tấn công tiêm sql hay không. Tôi đã cố gắng tìm các ví dụ về mã phá vỡ biện pháp an toàn này, nhưng tôi không thể tìm thấy bất kỳ ví dụ nào về nó.

Có ai ngoài đó có thể phá vỡ điều này không? Bạn sẽ làm điều này như thế nào?

CHỈNH SỬA: Để tóm tắt các câu trả lời cho đến nay:

  • Chưa ai tìm ra cách để vượt qua SafeDBString trên Sql Server 2005 hoặc 2008. Đó là tốt, tôi nghĩ?
  • Một số câu trả lời đã chỉ ra rằng bạn sẽ đạt được hiệu suất khi sử dụng các truy vấn được tham số hóa. Lý do là các kế hoạch truy vấn có thể được sử dụng lại.
  • Chúng tôi cũng đồng ý rằng việc sử dụng các truy vấn được tham số hóa cung cấp mã dễ đọc hơn và dễ bảo trì hơn
  • Hơn nữa, việc luôn sử dụng các tham số sẽ dễ dàng hơn là sử dụng các phiên bản SafeDBString khác nhau, chuyển đổi chuỗi thành số và chuyển đổi chuỗi ngày.
  • Sử dụng các tham số, bạn sẽ nhận được chuyển đổi kiểu tự động, một thứ đặc biệt hữu ích khi chúng tôi đang làm việc với ngày tháng hoặc số thập phân.
  • Và cuối cùng: Đừng cố tự bảo mật như JulianR đã viết. Các nhà cung cấp cơ sở dữ liệu dành nhiều thời gian và tiền bạc cho việc bảo mật. Không có cách nào chúng ta có thể làm tốt hơn và không có lý do gì chúng ta phải cố gắng làm công việc của họ.

Vì vậy, trong khi không ai có thể phá vỡ tính bảo mật đơn giản của hàm SafeDBString, tôi có rất nhiều lập luận tốt khác. Cảm ơn!


16
Các đồng nghiệp của bạn luôn đi theo cách riêng. Thách thức họ tìm một tác phẩm văn học ủng hộ vị trí của họ. Lập luận ex neolithos thật nực cười, mọi thứ thay đổi, chỉ một người mắc kẹt trong thời kỳ đồ đá mới không thích nghi được.
annakata

1
Chà, ít nhất các đồng nghiệp của bạn cũng bảo vệ khỏi MỘT trong các hình thức hack khác nhau ... Họ có chắc rằng tất cả các truy vấn tham số đều làm được không? (Tôi không ...)
Arjan Einbu

1
Bất kỳ một lỗ hổng nào sẽ không thuyết phục được họ. Nếu bạn mang theo một số lỗ hổng bảo mật (đó là những gì bạn đang yêu cầu) và các vấn đề khác và chỉ ra từng thông số rằng các thông số sẽ giải quyết vấn đề đó và nhóm của bạn sẽ phải viết hàng núi mã để cung cấp một phần chức năng, bạn có thể chiến thắng chúng. Chúc may mắn.
Robert Gowland

3
Ngay cả khi không có dấu ngoặc kép, bạn vẫn có thể phá vỡ mã của mình bằng logic. Hãy thử sử dụng tên người dùng "test OR 1 = 1" - bạn nhận được tất cả các hàng được trả về thay vì chỉ một hàng có kiểm tra tên người dùng!
Cầu

1
Thở dài. Tôi thực sự không hiểu chúng ta với tư cách là một ngành quản lý như thế nào để tiếp tục dung thứ cho loại hành vi thiếu chuyên nghiệp này.
jeroenh

Câu trả lời:


83

Tôi nghĩ câu trả lời chính xác là:

Đừng cố gắng tự bảo mật . Sử dụng bất kỳ thứ gì đáng tin cậy, thư viện tiêu chuẩn ngành có sẵn cho những gì bạn đang cố gắng làm, thay vì cố gắng tự làm. Bất kỳ giả định nào bạn đưa ra về bảo mật, có thể không chính xác. Có thể bảo mật như cách tiếp cận của riêng bạn (và tốt nhất là nó có vẻ không ổn), có nguy cơ bạn đang bỏ qua điều gì đó và bạn có thực sự muốn nắm lấy cơ hội đó khi nói đến bảo mật không?

Sử dụng các thông số.


Re "Sử dụng bất cứ thứ gì đáng tin cậy, thư viện tiêu chuẩn ngành có" - bạn có thể giới thiệu một cái cho .NET không? Có thể nhiều hơn một tùy thuộc vào DB: SQLServer, MySQL, PostgreSQL? Tôi đã tìm kiếm SQL-sanitizer nhưng không gặp nhiều may mắn, vì vậy hsve buộc phải triển khai của riêng tôi, tốt nhất có thể (điều này không còn nghi ngờ gì nữa).
PSU

72

Và sau đó ai đó sử dụng "thay vì". Các thông số là IMO, cách an toàn duy nhất để đi.

Nó cũng tránh rất nhiều vấn đề i18n với ngày / số; 01/02/03 là ngày gì? 123,456 là bao nhiêu? Các máy chủ của bạn (máy chủ ứng dụng và máy chủ db) có đồng ý với nhau không?

Nếu yếu tố rủi ro không thuyết phục họ, vậy còn hiệu suất thì sao? RDBMS có thể sử dụng lại kế hoạch truy vấn nếu bạn sử dụng các tham số, giúp hiệu suất. Nó không thể làm điều này chỉ với chuỗi.


Tôi đã thử các đối số định dạng và hiệu suất, nhưng chúng vẫn không thuyết phục.
Rune Grimstad

5
Trên thực tế, máy chủ sql có thể sử dụng lại kế hoạch truy vấn cho dù bạn có sử dụng tham số hay không. Tôi đồng ý với các đối số khác, nhưng đối với hầu hết các trường hợp, đối số hiệu suất cho sql được tham số hóa không bay nữa.
tnyfst

1
@tnyfst: nó có thể sử dụng lại kế hoạch thực thi khi chuỗi truy vấn thay đổi cho mọi sự kết hợp của các giá trị tham số không? Tôi không nghĩ rằng có thể.
John Saunders

4
Kế hoạch truy vấn sẽ được sử dụng lại nếu văn bản truy vấn là SẮC với văn bản truy vấn trước đó. Vì vậy, nếu bạn gửi truy vấn CHÍNH XÁC CÙNG hai lần, nó sẽ được sử dụng lại. Tuy nhiên, nếu bạn thay đổi thậm chí chỉ một khoảng trắng hoặc dấu phẩy hoặc một cái gì đó, một kế hoạch truy vấn mới sẽ phải được xác định.
marc_s

1
@Marc: Tôi không chắc bạn hoàn toàn chính xác. SQL Server caching hueristics hơi kỳ lạ. Trình phân tích cú pháp có khả năng xác định các hằng số trong văn bản và có thể chuyển đổi chuỗi SQL thành một chuỗi sử dụng các tham số một cách giả tạo. Sau đó, nó có thể chèn vào bộ nhớ cache văn bản của truy vấn được tham số mới này. SQL tương tự tiếp theo có thể tìm thấy phiên bản được tham số hóa của nó khớp trong bộ đệm. Tuy nhiên, các phiên bản được tham số hóa không phải lúc nào cũng được sử dụng với các phiên bản SQL ban đầu được lưu trong bộ nhớ cache, tôi nghi ngờ rằng SQL có một số lý do liên quan đến hiệu suất để chọn giữa hai cách tiếp cận.
AnthonyWJones

27

Cuộc tranh cãi không có lợi. Nếu bạn quản lý để tìm thấy một lỗ hổng, đồng nghiệp của bạn sẽ chỉ thay đổi chức năng SafeDBString để giải quyết nó và sau đó yêu cầu bạn chứng minh rằng nó không an toàn một lần nữa.

Cho rằng các truy vấn được tham số hóa là một phương pháp lập trình tốt nhất không thể tranh cãi, nên gánh nặng chứng minh cho họ để nêu rõ lý do tại sao họ không sử dụng một phương pháp vừa an toàn hơn vừa hoạt động tốt hơn.

Nếu vấn đề là viết lại tất cả mã kế thừa, thỏa hiệp dễ dàng sẽ là sử dụng các truy vấn được tham số hóa trong tất cả mã mới và cấu trúc lại mã cũ để sử dụng chúng khi làm việc trên mã đó.

Tôi đoán là vấn đề thực tế là lòng kiêu hãnh và sự bướng bỉnh, và bạn không thể làm gì hơn được nữa.


19

Trước hết, mẫu của bạn cho phiên bản "Thay thế" là sai. Bạn cần đặt dấu nháy đơn xung quanh văn bản:

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);

Vì vậy, đó là một điều khác mà các tham số làm cho bạn: bạn không cần phải lo lắng về việc liệu một giá trị có cần được đặt trong dấu ngoặc kép hay không. Tất nhiên, bạn có thể xây dựng điều đó vào hàm, nhưng sau đó bạn cần thêm rất nhiều độ phức tạp vào hàm: làm thế nào để biết sự khác biệt giữa 'NULL' là null và 'NULL' chỉ là một chuỗi hoặc giữa một số và một chuỗi chỉ xảy ra chứa rất nhiều chữ số. Nó chỉ là một nguồn khác cho lỗi.

Một điều khác là hiệu suất: các kế hoạch truy vấn được tham số hóa thường được lưu vào bộ nhớ đệm tốt hơn các kế hoạch được nối, do đó có thể tiết kiệm cho máy chủ một bước khi chạy truy vấn.

Ngoài ra, việc thoát khỏi các dấu ngoặc kép là không đủ tốt. Nhiều sản phẩm DB cho phép các phương pháp thay thế để thoát các ký tự mà kẻ tấn công có thể lợi dụng. Trong MySQL, ví dụ, bạn cũng có thể thoát khỏi một trích dẫn duy nhất có dấu gạch chéo ngược. Và do đó, giá trị "name" sau đây sẽ làm nổ tung MySQL chỉ với SafeDBString()hàm, bởi vì khi bạn nhân đôi dấu nháy đơn, dấu đầu tiên vẫn bị thoát ra bởi dấu gạch chéo ngược, để lại dấu thứ hai "hoạt động":

x \ 'HOẶC 1 = 1; -


Ngoài ra, JulianR đưa ra một điểm tốt bên dưới: ĐỪNG BAO GIỜ tự mình cố gắng làm công việc bảo mật. Thật dễ dàng để lập trình bảo mật sai theo những cách tinh vi có vẻ hoạt động, ngay cả khi đã kiểm tra kỹ lưỡng. Sau đó, thời gian trôi qua và một năm sau, bạn phát hiện ra hệ thống của mình đã bị bẻ khóa sáu tháng trước và bạn thậm chí chưa bao giờ biết điều đó cho đến tận lúc đó.

Luôn dựa nhiều nhất có thể vào các thư viện bảo mật được cung cấp cho nền tảng của bạn. Chúng sẽ được viết bởi những người làm mã bảo mật để kiếm sống, được kiểm tra tốt hơn nhiều so với những gì bạn có thể quản lý và được bảo dưỡng bởi nhà cung cấp nếu tìm thấy lỗ hổng.


5
Hàm thay thế thêm dấu nháy đơn
Rune Grimstad

5
Sau đó, nó chỉ là một nguồn lỗi nữa. Làm thế nào nó biết sự khác biệt giữa NULL dưới dạng giá trị null và NULL dưới dạng chuỗi văn bản? Hay giữa một đầu vào số và một chuỗi chỉ chứa các chữ số?
Joel Coehoorn

Điểm tốt. Bạn chỉ nên sử dụng hàm cho chuỗi và có thể là ngày tháng, vì vậy bạn phải cẩn thận. Đó là một lý do nữa để sử dụng các tham số! Yay!
Rune Grimstad

10

Vì vậy, tôi muốn nói:

1) Tại sao bạn lại cố gắng triển khai lại thứ gì đó đã được tích hợp sẵn? nó ở đó, sẵn có, dễ sử dụng và đã được gỡ lỗi trên quy mô toàn cầu. Nếu các lỗi trong tương lai được tìm thấy trong đó, chúng sẽ được sửa và có sẵn cho mọi người rất nhanh chóng mà bạn không cần phải làm gì cả.

2) Những quy trình nào được thực hiện để đảm bảo rằng bạn không bao giờ bỏ lỡ cuộc gọi đến SafeDBString? Thiếu nó chỉ ở 1 nơi có thể mở ra hàng loạt vấn đề. Bạn sẽ đánh mắt những điều này đến mức nào, và hãy cân nhắc xem nỗ lực đó đã lãng phí bao nhiêu khi câu trả lời đúng được chấp nhận quá dễ dàng đạt được.

3) Bạn có chắc chắn rằng bạn đã che đậy mọi vectơ tấn công mà Microsoft (tác giả của DB và thư viện truy cập) biết trong quá trình triển khai SafeDBString của bạn ...

4) Làm thế nào dễ dàng để đọc cấu trúc của sql? Ví dụ sử dụng + nối, các tham số rất giống string.Format, dễ đọc hơn.

Ngoài ra, có 2 cách để tìm ra những gì đã thực sự chạy - sử dụng hàm LogCommand của riêng bạn, một hàm đơn giản không có mối quan tâm về bảo mật hoặc thậm chí xem xét dấu vết sql để tìm ra những gì cơ sở dữ liệu nghĩ đang thực sự diễn ra.

Hàm LogCommand của chúng tôi chỉ đơn giản là:

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }

Đúng hay sai, nó cung cấp cho chúng tôi thông tin chúng tôi cần mà không có vấn đề bảo mật.


1
Anh ta có thể phải đối phó với một loạt các lập trình viên VBSCRIPT cũ, những người đã quen làm mọi thứ, bao gồm cả XML và SQL, thông qua nối chuỗi. Đây sẽ là những người sợ hãi khi sử dụng API. Không có gì có thể làm được với họ, ít nhất là không có gì nhân đạo.
John Saunders

1
+1 cho mục số 2, ngoại trừ việc không có cách nào để thực thi các thông số thực.
Joel Coehoorn

7

Với các truy vấn được tham số hóa, bạn nhận được nhiều hơn sự bảo vệ chống lại việc tiêm sql. Bạn cũng nhận được tiềm năng lưu trữ kế hoạch thực thi tốt hơn. Nếu bạn sử dụng trình biên dịch truy vấn máy chủ sql, bạn vẫn có thể thấy 'sql chính xác được chạy trên cơ sở dữ liệu', vì vậy bạn cũng không thực sự mất gì về việc gỡ lỗi các câu lệnh sql của mình.


MySQL cũng ghi nhật ký các truy vấn được tham số hóa với các giá trị tham số được nội suy vào chúng.
Bill Karwin

5

Tôi đã sử dụng cả hai cách tiếp cận để tránh các cuộc tấn công SQL injection và chắc chắn thích các truy vấn được tham số hóa. Khi tôi sử dụng các truy vấn nối, tôi đã sử dụng một hàm thư viện để thoát các biến (như mysql_real_escape_string) và sẽ không tự tin rằng tôi đã bao gồm mọi thứ trong một triển khai độc quyền (có vẻ như bạn cũng vậy).


2
+1 vì mysql_real_escape_string () thoát khỏi \ x00, \ x1a, \ n \ r 'và ". Nó cũng xử lý các vấn đề về bộ ký tự. Hàm ngây thơ của đồng nghiệp OP không làm được điều đó!
Bill Karwin

4

Bạn không thể dễ dàng thực hiện bất kỳ loại kiểm tra đầu vào của người dùng mà không sử dụng các tham số.

Nếu bạn sử dụng các lớp SQLCommand và SQLParameter để thực hiện các cuộc gọi DB, bạn vẫn có thể thấy truy vấn SQL đang được thực thi. Nhìn vào thuộc tính CommandText của SQLCommand.

Tôi luôn nghi ngờ về cách tiếp cận của riêng bạn để ngăn chặn việc đưa vào SQL khi các truy vấn được tham số hóa quá dễ sử dụng. Thứ hai, chỉ vì "nó luôn được thực hiện theo cách đó" không có nghĩa đó là cách làm đúng.


3

Điều này chỉ an toàn nếu bạn được đảm bảo rằng bạn sẽ chuyển qua một chuỗi.

Điều gì xảy ra nếu bạn không chuyển vào một chuỗi tại một thời điểm nào đó? Điều gì sẽ xảy ra nếu bạn chỉ vượt qua một con số?

http://www.mywebsite.com/profile/?id=7;DROP DATABASE DB

Cuối cùng sẽ trở thành:

SELECT * FROM DB WHERE Id = 7;DROP DATABASE DB

Đó là một chuỗi hoặc một số. Một chuỗi được thoát bằng SafeDbString. Một số là Int32 và nó không thể bỏ cơ sở dữ liệu.
Andomar

Các số dễ xử lý hơn. Bạn chỉ cần chuyển đổi tham số thành int / float / bất cứ thứ gì trước khi sử dụng nó trong truy vấn. Vấn đề là khi nào bạn phải chấp nhận dữ liệu chuỗi.
Rune Grimstad

Andomar - nếu bạn chỉ xây dựng một câu lệnh SQL bằng tay thì "kiểu" dự định không quan trọng, bạn có thể chèn SQL với một số rất, rất dễ dàng. Rune - Tôi nghĩ rằng điều này phụ thuộc quá nhiều vào nhà phát triển cá nhân để nhớ tất cả các sắc thái của việc giải quyết SQL injection theo cách thủ công. Nếu bạn chỉ nói "sử dụng các tham số" thì rất đơn giản và chúng không thể sai được.
joshcomley

@Andomar: Còn NULL thì sao? Hoặc các chuỗi trông giống như số?
Joel Coehoorn

2

Tôi sẽ sử dụng các thủ tục hoặc hàm được lưu trữ cho mọi thứ, vì vậy câu hỏi sẽ không nảy sinh.

Khi tôi phải đặt SQL vào mã, tôi sử dụng các tham số, đó là điều duy nhất có ý nghĩa. Nhắc những người bất đồng chính kiến ​​rằng có những tin tặc thông minh hơn họ và có động cơ tốt hơn để phá mã đang cố gắng lợi dụng họ. Sử dụng các tham số, đơn giản là không thể, và cũng không khó.


Ok, làm thế nào để thực hiện việc tiêm SQL bằng cách sử dụng các tham số?
John Saunders

@Saunders: Bước 1 là tìm lỗi tràn bộ đệm trong chức năng xử lý tham số của DB của bạn.
Brian

2
Tìm thấy một chưa? Trong một DB thương mại đang bị tấn công bởi hàng trăm nghìn tin tặc hàng ngày? Một cái được tạo ra bởi một công ty phần mềm được biết là có túi tiền rất sâu? Bạn có thể trích dẫn vụ kiện theo tên nếu điều này có thể.
John Saunders

1
Tất nhiên, nếu SPROC sử dụng nối và EXEC (thay vì sp_ExecuteSQL) thì bạn sẽ gặp rắc rối ... (Tôi đã thấy nó làm sai quá nhiều lần để giảm giá nó ...)
Marc Gravell

2

Đồng ý hoàn toàn về các vấn đề bảo mật.
Một lý do khác để sử dụng các tham số là vì hiệu quả.

Cơ sở dữ liệu sẽ luôn biên dịch truy vấn của bạn và lưu vào bộ nhớ cache, sau đó sử dụng lại truy vấn đã lưu trong bộ nhớ cache (rõ ràng là nhanh hơn cho các yêu cầu tiếp theo). Nếu bạn sử dụng các tham số thì ngay cả khi bạn sử dụng các tham số khác nhau, cơ sở dữ liệu sẽ sử dụng lại truy vấn đã lưu trong bộ nhớ cache của bạn vì nó khớp dựa trên chuỗi SQL trước khi liên kết các tham số.

Tuy nhiên, nếu bạn không ràng buộc các tham số thì chuỗi SQL sẽ thay đổi theo mọi yêu cầu (có các tham số khác nhau) và nó sẽ không bao giờ khớp với những gì trong bộ nhớ cache của bạn.


2

Vì những lý do đã được đưa ra, các thông số là một ý tưởng rất tốt. Nhưng chúng tôi ghét sử dụng chúng vì việc tạo tham số và gán tên của nó cho một biến để sử dụng sau này trong một truy vấn là một sự phá hoại đầu ba hướng.

Lớp sau bao bọc trình tạo chuỗi mà bạn thường sử dụng để xây dựng các yêu cầu SQL. Nó cho phép bạn viết các truy vấn paramateised mà không cần phải tạo một tham số , vì vậy bạn có thể tập trung vào SQL. Mã của bạn sẽ trông như thế này ...

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId, SqlDbType.Int);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName, SqlDbType.NVarChar);
myCommand.CommandText = bldr.ToString();

Tôi hy vọng bạn đồng ý rằng khả năng đọc mã được cải thiện đáng kể và đầu ra là một truy vấn được tham số hóa thích hợp.

Lớp học trông như thế này ...

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

namespace myNamespace
{
    /// <summary>
    /// Pour le confort et le bonheur, cette classe remplace StringBuilder pour la construction
    /// des requêtes SQL, avec l'avantage qu'elle gère la création des paramètres via la méthode
    /// Value().
    /// </summary>
    public class SqlBuilder
    {
        private StringBuilder _rq;
        private SqlCommand _cmd;
        private int _seq;
        public SqlBuilder(SqlCommand cmd)
        {
            _rq = new StringBuilder();
            _cmd = cmd;
            _seq = 0;
        }
        //Les autres surcharges de StringBuilder peuvent être implémenté ici de la même façon, au besoin.
        public SqlBuilder Append(String str)
        {
            _rq.Append(str);
            return this;
        }
        /// <summary>
        /// Ajoute une valeur runtime à la requête, via un paramètre.
        /// </summary>
        /// <param name="value">La valeur à renseigner dans la requête</param>
        /// <param name="type">Le DBType à utiliser pour la création du paramètre. Se référer au type de la colonne cible.</param>
        public SqlBuilder Value(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append(paramName);
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this;
        }
        public SqlBuilder FuzzyValue(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append("'%' + " + paramName + " + '%'");
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this; 
        }

        public override string ToString()
        {
            return _rq.ToString();
        }
    }
}

1

Từ khoảng thời gian ngắn tôi phải điều tra các vấn đề về SQL injection, tôi có thể thấy rằng việc tạo ra một giá trị 'an toàn' cũng có nghĩa là bạn đang đóng cửa cho các tình huống mà bạn có thể thực sự muốn dấu nháy đơn trong dữ liệu của mình - còn tên của ai đó thì sao , ví dụ: O'Reilly.

Điều đó để lại các tham số và thủ tục được lưu trữ.

Và vâng, bạn nên luôn cố gắng triển khai mã theo cách tốt nhất mà bạn biết bây giờ - không chỉ cách nó luôn được thực hiện.


Dấu nháy đơn kép sẽ được máy chủ sql dịch thành một dấu nháy đơn, vì vậy O'Reilly sẽ được dịch thành Name = 'O''Reilly'
Rune Grimstad

Vậy có một chức năng tương ứng để loại bỏ dấu nháy đơn khi người dùng muốn xem dữ liệu của họ?
quamrana

Không cần. Chuỗi thoát cho phép trình phân tích cú pháp nhìn thấy một trích dẫn duy nhất chứ không phải cuối chuỗi. Vì nó phân tích cú pháp, nó được coi ''là một ký tự ', vì vậy chuỗi của bạn sẽ được xem bên trong là chuỗi ký tự O'Reilly. Đó là những gì DB sẽ lưu trữ, truy xuất, so sánh với, v.v. Nếu bạn muốn hiển thị cho người dùng dữ liệu của họ sau khi bạn đã thoát nó, thì hãy giữ một bản sao của chuỗi không thoát bên cạnh.
cHao

1

Dưới đây là một số bài báo mà bạn có thể thấy hữu ích trong việc thuyết phục đồng nghiệp của mình.

http://www.sommarskog.se/dynamic_sql.html

http://unixwiz.net/techtips/sql-injection.html

Cá nhân tôi muốn không bao giờ cho phép bất kỳ mã động nào chạm vào cơ sở dữ liệu của mình, yêu cầu tất cả các liên hệ phải thông qua sp (và không phải mã sử dụng SQl động). Điều này có nghĩa là không có gì ngoại trừ những gì tôi đã cho phép người dùng làm có thể được thực hiện và người dùng nội bộ (ngoại trừ một số rất ít có quyền truy cập sản xuất cho mục đích quản trị) không thể truy cập trực tiếp vào bảng của tôi và tạo ra sự tàn phá, đánh cắp dữ liệu hoặc thực hiện hành vi gian lận. Nếu bạn chạy một ứng dụng tài chính, đây là cách an toàn nhất.


1

Nó có thể bị hỏng, tuy nhiên phương tiện phụ thuộc vào các phiên bản / bản vá chính xác, v.v.

Một lỗi đã được đưa ra là lỗi tràn / cắt bớt có thể bị khai thác.

Một phương tiện trong tương lai sẽ được tìm lỗi tương tự như cơ sở dữ liệu khác - ví dụ stack MySQL / PHP bị một vấn đề thoát vì chuỗi UTF8 nhất định có thể được sử dụng để thao tác các chức năng thay thế - sự thay thế chức năng sẽ bị lừa vào giới thiệu các nhân vật tiêm.

Vào cuối ngày, cơ chế bảo mật thay thế dựa vào chức năng dự kiến nhưng không dự kiến . Vì chức năng không phải là mục đích dự kiến ​​của mã, nên có khả năng cao rằng một số điều kỳ quặc được phát hiện sẽ phá vỡ chức năng mong đợi của bạn.

Nếu bạn có nhiều mã kế thừa, phương pháp thay thế có thể được sử dụng làm bản đồ dừng để tránh việc viết lại và kiểm tra dài dòng. Nếu bạn đang viết mã mới, không có lý do gì cả.


1

Luôn sử dụng các truy vấn được tham số hóa nếu có thể. Đôi khi, ngay cả một đầu vào đơn giản mà không sử dụng bất kỳ ký tự lạ nào cũng có thể tạo SQL-injection nếu nó không được xác định là đầu vào cho một trường trong cơ sở dữ liệu.

Vì vậy, hãy để cơ sở dữ liệu tự làm công việc xác định đầu vào, chưa kể nó còn giúp tiết kiệm nhiều rắc rối khi bạn cần thực sự chèn các ký tự kỳ lạ mà nếu không sẽ bị thoát hoặc thay đổi. Nó thậm chí có thể tiết kiệm một số thời gian chạy có giá trị cuối cùng mà không cần phải tính toán đầu vào.


1

Tôi không thấy bất kỳ câu trả lời nào khác giải quyết mặt này của câu hỏi 'tại sao tự làm điều đó là xấu', nhưng hãy xem xét một cuộc tấn công Cắt bớt SQL .

Ngoài ra còn có hàm QUOTENAMET-SQL có thể hữu ích nếu bạn không thể thuyết phục họ sử dụng tham số. Nó nắm bắt được rất nhiều (tất cả?) Những mối quan tâm hàng đầu đã thoát ra.


1

2 năm sau , tôi tái phạm ... Bất cứ ai tìm thấy thông số là một vấn đề đáng tiếc , đều có thể dùng thử Phần mở rộng VS của tôi, QueryFirst . Bạn chỉnh sửa yêu cầu của mình trong một tệp .sql thực (Xác thực, Intellisense). Để thêm một tham số, bạn chỉ cần nhập trực tiếp vào SQL của mình, bắt đầu bằng ký tự '@'. Khi bạn lưu tệp, QueryFirst sẽ tạo các lớp trình bao bọc để cho phép bạn chạy truy vấn và truy cập kết quả. Nó sẽ tra cứu kiểu DB của tham số của bạn và ánh xạ nó thành một kiểu .net, mà bạn sẽ tìm thấy như một đầu vào cho các phương thức Execute () đã tạo. Không thể đơn giản hơn. Thực hiện theo đúng cách hoàn toàn nhanh chóng và dễ dàng hơn bất kỳ cách nào khác, và việc tạo ra một lỗ hổng tiêm sql trở nên không thể, hoặc ít nhất là rất khó. Có những lợi thế tuyệt vời khác, như có thể xóa các cột trong DB của bạn và ngay lập tức thấy các lỗi biên dịch trong ứng dụng của bạn.

tuyên bố từ chối trách nhiệm pháp lý: Tôi đã viết QueryFirst


0

Dưới đây là một số lý do để sử dụng truy vấn được tham số hóa:

  1. Bảo mật - Lớp truy cập cơ sở dữ liệu biết cách loại bỏ hoặc thoát các mục không được phép trong dữ liệu.
  2. Tách các mối quan tâm - Mã của tôi không chịu trách nhiệm chuyển đổi dữ liệu thành định dạng mà cơ sở dữ liệu thích.
  3. Không dư thừa - Tôi không cần bao gồm một assembly hoặc lớp trong mọi dự án thực hiện định dạng / thoát cơ sở dữ liệu này; nó được tích hợp vào thư viện lớp.

0

Có một vài lỗ hổng (tôi không thể nhớ đó là cơ sở dữ liệu nào) có liên quan đến lỗi tràn bộ đệm của câu lệnh SQL.

Điều tôi muốn nói là, SQL-Injection không chỉ là "thoát khỏi câu trích dẫn", và bạn không biết điều gì sẽ xảy ra tiếp theo.


0

Một cân nhắc quan trọng khác là theo dõi dữ liệu đã thoát và chưa thoát. Có rất nhiều ứng dụng, Web và các ứng dụng khác, dường như không theo dõi chính xác thời điểm dữ liệu là thô-Unicode, & -encoded, HTML được định dạng, v.v. Rõ ràng là sẽ rất khó để theo dõi chuỗi nào được ''–encode và chuỗi nào không.

Đó cũng là một vấn đề khi bạn cuối cùng thay đổi kiểu của một số biến - có lẽ nó từng là một số nguyên, nhưng bây giờ nó là một chuỗi. Bây giờ bạn có một vấn đề.

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.