Tại sao cơ chế ngăn chặn tiêm SQL phát triển theo hướng sử dụng các truy vấn tham số?


59

Theo cách tôi thấy, các cuộc tấn công tiêm SQL có thể được ngăn chặn bằng cách:

  1. Sàng lọc cẩn thận, lọc, mã hóa đầu vào (trước khi chèn vào SQL)
  2. Sử dụng các câu lệnh / truy vấn tham số đã chuẩn bị

Tôi cho rằng có những ưu và nhược điểm đối với từng loại, nhưng tại sao # 2 lại cất cánh và được coi là ít nhiều là cách thực tế để ngăn chặn các cuộc tấn công tiêm chích? Có phải nó chỉ an toàn hơn và ít bị lỗi hơn hoặc có các yếu tố khác?

Theo tôi hiểu, nếu số 1 được sử dụng đúng cách và tất cả các cảnh báo đều được quan tâm, thì nó có thể hiệu quả như số 2.

Vệ sinh, Lọc và Mã hóa

Có một số nhầm lẫn về phía tôi giữa việc vệ sinh , lọcmã hóa có nghĩa là gì. Tôi sẽ nói rằng vì mục đích của tôi, tất cả những điều trên có thể được xem xét cho tùy chọn 1. Trong trường hợp này tôi hiểu rằng vệ sinh và lọc có khả năng sửa đổi hoặc loại bỏ dữ liệu đầu vào, trong khi mã hóa bảo tồn dữ liệu như hiện tại , nhưng mã hóa nó đúng cách để tránh các cuộc tấn công tiêm. Tôi tin rằng việc thoát dữ liệu có thể được coi là một cách mã hóa nó.

Truy vấn tham số so với thư viện mã hóa

Có câu trả lời trong đó các khái niệm parameterized queriesencoding librariesđược điều trị thay thế cho nhau. Sửa lỗi cho tôi nếu tôi sai, nhưng tôi có ấn tượng rằng chúng khác nhau.

Tôi hiểu rằng encoding libraries, dù họ có tiềm năng sửa đổi "Chương trình" SQL tốt đến đâu, bởi vì họ đang thực hiện các thay đổi đối với chính SQL, trước khi nó được gửi đến RDBMS.

Parameterized queries mặt khác, gửi chương trình SQL đến RDBMS, sau đó tối ưu hóa truy vấn, xác định kế hoạch thực hiện truy vấn, chọn các chỉ mục sẽ được sử dụng, v.v., sau đó cắm dữ liệu, là bước cuối cùng bên trong RDBMS Chính nó.

Thư viện mã hóa

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

Truy vấn tham số

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

Ý nghĩa lịch sử

Một số câu trả lời đề cập rằng trong lịch sử, các truy vấn được tham số hóa (PQ) đã được tạo vì lý do hiệu suất và trước khi các cuộc tấn công tiêm chích mà các vấn đề mã hóa nhắm mục tiêu trở nên phổ biến. Tại một số điểm, rõ ràng là PQ cũng khá hiệu quả để chống lại các cuộc tấn công tiêm. Để giữ đúng tinh thần của câu hỏi của tôi, tại sao PQ vẫn là phương pháp được lựa chọn và tại sao nó lại phát triển vượt bậc so với hầu hết các phương pháp khác khi nói đến việc ngăn chặn các cuộc tấn công tiêm nhiễm SQL?


1
Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
maple_shaft

23
Các tuyên bố đã chuẩn bị không phải là kết quả của sự tiến hóa từ các cuộc tấn công tiêm nhiễm SQL. Họ đã ở đó từ đầu. Câu hỏi của bạn dựa trên một tiền đề sai.
dùng207421

4
Nếu bạn nghĩ rằng bạn thông minh hơn những kẻ xấu thì hãy tham gia # 1
paparazzo

1
"Tại sao PQ vẫn là phương pháp được lựa chọn" Bởi vì nó dễ nhất và mạnh nhất. Cộng với những lợi thế về hiệu suất đã nói ở trên đối với PQ. Thực sự không có nhược điểm.
Paul Draper

1
Bởi vì đó là giải pháp chính xác cho vấn đề làm thế nào để thực hiện các truy vấn, ngay cả khi đó không phải là vấn đề tiêm SQL trong bối cảnh bảo mật . Các biểu mẫu yêu cầu thoát và sử dụng dữ liệu trong băng tần với các lệnh luôn là một lỗi thiết kế vì chúng dễ bị lỗi, phản trực giác và bị hỏng khi sử dụng sai. Xem thêm: kịch bản shell.
R ..

Câu trả lời:


147

Vấn đề là # 1 yêu cầu bạn phân tích và giải thích hiệu quả toàn bộ biến thể SQL mà bạn đang làm việc để bạn biết nếu nó đang làm điều gì đó không nên. Và luôn cập nhật mã đó khi bạn cập nhật cơ sở dữ liệu của mình. Ở mọi nơi bạn chấp nhận đầu vào cho các truy vấn của bạn. không vít nó lên.

Vì vậy, có, loại điều đó sẽ ngăn chặn các cuộc tấn công tiêm nhiễm SQL, nhưng việc thực hiện sẽ tốn kém hơn một cách vô lý.


60
@dennis - Chà, một trích dẫn trong biến thể SQL của bạn là gì? "? '? TIẾNG? U + 2018? \ U2018? Có thủ thuật nào để tách các biểu thức không? Các truy vấn con của bạn có thể cập nhật không? Có nhiều điều cần xem xét.
Telastyn

7
@Dennis mỗi công cụ DB có cách làm việc riêng như thoát các ký tự trong chuỗi. Đó là rất nhiều lỗ hổng để cắm, đặc biệt là nếu một ứng dụng cần hoạt động với nhiều công cụ DB hoặc tương thích với các phiên bản tương lai của cùng một công cụ có thể thay đổi một số cú pháp truy vấn nhỏ có thể khai thác được.

12
Một lợi ích khác của các câu lệnh được chuẩn bị là hiệu suất bạn đạt được khi bạn phải chạy lại cùng một truy vấn, với các giá trị khác nhau. Ngoài ra, các câu lệnh được chuẩn bị có thể biết nếu một giá trị thực sự có nghĩa là null, một chuỗi hoặc một số và hành động tương ứng. Điều này rất tốt cho an ninh. Và ngay cả khi bạn chạy truy vấn một lần, công cụ DB sẽ có nó được tối ưu hóa cho bạn. Tốt hơn nữa nếu nó được lưu trữ!
Ismael Miguel

8
@Dennis Ông Henry Null sẽ cảm ơn bạn vì đã làm điều này đúng cách.
Mathieu Guindon

14
@Dennis tên đầu tiên là không liên quan. Vấn đề là với tên cuối cùng. Xem Stack Overflow , Lập trình viên.SE , Fox Sports , Wired , BBC và bất cứ điều gì khác mà bạn có thể tìm thấy trong một tìm kiếm nhanh của Google ;-)
Mathieu Guindon

80

Bởi vì phương án 1 không phải là một giải pháp. Sàng lọc và lọc có nghĩa là từ chối hoặc loại bỏ đầu vào không hợp lệ. Nhưng bất kỳ đầu vào có thể là hợp lệ. Ví dụ, dấu nháy đơn là một ký tự hợp lệ trong tên "O'Malley". Nó chỉ cần được mã hóa chính xác trước khi được sử dụng trong SQL, đây là điều mà các câu lệnh được chuẩn bị thực hiện.


Sau khi bạn thêm ghi chú, có vẻ như về cơ bản bạn đang hỏi tại sao sử dụng chức năng thư viện tiêu chuẩn thay vì viết mã tương tự về chức năng của riêng bạn từ đầu? Bạn nên luôn luôn thích các giải pháp thư viện tiêu chuẩn để viết mã của riêng bạn. Đó là công việc ít hơn và dễ bảo trì hơn. Đây là trường hợp cho bất kỳ chức năng nào , nhưng đặc biệt đối với một thứ nhạy cảm về bảo mật, hoàn toàn không có ý nghĩa để tự mình phát minh lại bánh xe.


2
Đó là nó (và đó là phần còn thiếu trong hai câu trả lời khác, vì vậy +1). Cho biết cách đặt câu hỏi, nó không phải là về vệ sinh đầu vào của người dùng, nhưng, tôi trích dẫn câu hỏi: Đầu vào lọc lọc (trước khi chèn). Nếu bây giờ câu hỏi là về vệ sinh đầu vào, vậy thì tại sao bạn lại tự làm điều đó thay vì để thư viện làm điều đó (trong khi, đồng thời, cũng mất cơ hội để có kế hoạch thực hiện được lưu trong bộ nhớ cache)?
Arseni Mourzenko

8
@Dennis: Vệ sinh hoặc lọc nghĩa là xóa thông tin. Mã hóa có nghĩa là chuyển đổi biểu diễn dữ liệu mà không mất thông tin.
JacquesB

9
@Dennis: lọc có nghĩa là chấp nhận hoặc từ chối đầu vào của người dùng. Chẳng hạn, sẽ được lọc như là đầu vào của trường tuổi người dùng, vì giá trị này không hợp lệ. Ví dụ, nếu thay vì lọc đầu vào, bạn bắt đầu chuyển đổi nó bằng cách thay thế ký tự trích dẫn đơn, thì bạn đang thực hiện chính xác như các thư viện cơ sở dữ liệu nơi chúng sử dụng truy vấn tham số; trong trường hợp này, câu hỏi của bạn chỉ đơn giản là Tại sao tôi sẽ sử dụng một cái gì đó tồn tại và được viết bởi các chuyên gia trong lĩnh vực này, khi tôi có thể phát minh lại bánh xe trong mọi dự án?
Asi Mourzenko

3
@Dennis: O\'Malleyđang sử dụng dấu gạch chéo để thoát trích dẫn để chèn đúng (ít nhất là trong một số cơ sở dữ liệu). Trong MS SQL hoặc Access, nó có thể được thoát bằng một trích dẫn bổ sung O''Malley. Không phải rất di động nếu bạn phải tự làm điều đó.
AbraCadaver

5
Tôi không thể nói cho bạn biết bao nhiêu lần tên của tôi đã bị hệ thống từ chối hoàn toàn. Đôi khi, tôi thậm chí đã nhìn thấy các lỗi gây ra bởi SQL tiêm chỉ từ việc sử dụng tên của mình. Heck, tôi đã từng được yêu cầu thay đổi tên người dùng của tôi bởi vì tôi thực sự đã phá vỡ một cái gì đó trên phụ trợ.
Alexander O'Mara

60

Nếu bạn đang cố xử lý chuỗi, thì bạn không thực sự tạo truy vấn SQL. Bạn đang tạo một chuỗi có thể tạo ra một truy vấn SQL. Có một mức độ gián tiếp mở ra rất nhiều chỗ cho các lỗi và lỗi. Thực sự hơi ngạc nhiên, vì trong hầu hết các bối cảnh, chúng tôi rất vui khi được tương tác với một cái gì đó theo chương trình. Ví dụ: nếu chúng tôi có một số cấu trúc danh sách và muốn thêm một mục, chúng tôi thường không làm:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Nếu ai đó đề nghị làm điều đó, bạn sẽ trả lời đúng rằng điều đó khá vô lý, và người đó chỉ nên làm:

List<Integer> list = /* ... */;
list.add(5, position=2);

Điều đó tương tác với cấu trúc dữ liệu ở cấp độ khái niệm của nó. Nó không giới thiệu bất kỳ sự phụ thuộc nào vào cách cấu trúc đó có thể được in hoặc phân tích cú pháp. Đó là những quyết định hoàn toàn trực giao.

Cách tiếp cận đầu tiên của bạn giống như mẫu đầu tiên (chỉ kém hơn một chút): bạn cho rằng có thể xây dựng chuỗi theo cách lập trình sẽ được phân tích cú pháp chính xác như truy vấn mà bạn muốn. Điều đó phụ thuộc vào trình phân tích cú pháp và toàn bộ logic xử lý chuỗi.

Cách tiếp cận thứ hai của việc sử dụng các truy vấn đã chuẩn bị giống như mẫu thứ hai. Khi bạn sử dụng truy vấn đã chuẩn bị, về cơ bản, bạn phân tích cú pháp truy vấn giả hợp pháp nhưng có một số chỗ dành sẵn trong đó, sau đó sử dụng API để thay thế chính xác một số giá trị trong đó. Bạn không còn liên quan đến quá trình phân tích cú pháp và bạn không phải lo lắng về bất kỳ xử lý chuỗi nào.

Nói chung, dễ dàng hơn nhiều và ít xảy ra lỗi hơn khi tương tác với mọi thứ ở cấp độ khái niệm của chúng. Một truy vấn không phải là một chuỗi, một truy vấn là những gì bạn nhận được khi phân tích một chuỗi hoặc xây dựng một chuỗi theo chương trình (hoặc bất kỳ phương thức nào khác cho phép bạn tạo một chuỗi).

Có một sự tương đồng tốt ở đây giữa các macro kiểu C thực hiện thay thế văn bản đơn giản và các macro kiểu Lisp tạo mã tùy ý. Với các macro kiểu C, bạn có thể thay thế văn bản trong mã nguồn và điều đó có nghĩa là bạn có khả năng đưa ra các lỗi cú pháp hoặc hành vi gây hiểu lầm. Với macro Lisp, bạn đang tạo mã ở dạng trình biên dịch xử lý nó (nghĩa là bạn đang trả về các cấu trúc dữ liệu thực tế mà trình biên dịch xử lý, chứ không phải văn bản mà trình đọc phải xử lý trước khi trình biên dịch có thể xử lý nó) . Tuy nhiên, với macro Lisp, bạn không thể tạo ra thứ gì đó có thể là lỗi phân tích cú pháp. Ví dụ: bạn không thể tạo (hãy (ab) a .

Ngay cả với các macro Lisp, bạn vẫn có thể tạo mã xấu, bởi vì bạn không nhất thiết phải nhận thức được cấu trúc được cho là có ở đó. Ví dụ, trong Lisp, (let ((ab)) a) có nghĩa là "thiết lập một ràng buộc từ vựng mới của biến a với giá trị của biến b, và sau đó trả về giá trị của a", và (let (ab) a) có nghĩa là "thiết lập các ràng buộc từ vựng mới của các biến a và b và khởi tạo cả hai cho đến 0 và sau đó trả về giá trị của a." Cả hai đều đúng về mặt cú pháp, nhưng chúng có nghĩa là những thứ khác nhau. Để tránh vấn đề này, bạn có thể sử dụng các hàm nhận biết ngữ nghĩa nhiều hơn và làm một cái gì đó như:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

Với những thứ tương tự, không thể trả lại thứ gì đó không hợp lệ về mặt cú pháp và khó có thể trả lại thứ gì đó vô tình không như bạn muốn.


Lời giải thích hay!
Mike Partridge

2
Bạn đã đánh mất tôi ở "sự tương tự tốt" nhưng tôi đã nâng cao dựa trên lời giải thích trước đó. :)
tự đại diện

1
Ví dụ tuyệt vời! - Và bạn có thể thêm: Tùy thuộc vào kiểu dữ liệu, đôi khi thậm chí không thể hoặc không khả thi để tạo một chuỗi có thể phân tích cú pháp. - Điều gì xảy ra nếu một trong các tham số của tôi là trường văn bản tự do chứa bản nháp câu chuyện (~ 10.000 ký tự)? hoặc nếu một tham số là JPG-Image thì sao? - Cách duy nhất sau đó là truy vấn tham số
Falco

Trên thực tế không - đó là một mô tả khá xấu về lý do tại sao các tuyên bố được chuẩn bị phát triển như là một biện pháp phòng thủ để tiêm sql. Đặc biệt được đưa ra ví dụ mã là trong java, không xuất hiện khi các truy vấn được tham số hóa có khả năng phát triển trong khung thời gian trong đó C / C ++ được coi là trạng thái của nghệ thuật. Cơ sở dữ liệu SQL bắt đầu được sử dụng trong những năm đầu của khung thời gian 1970-1980. CÁCH trước các ngôn ngữ cấp cao hơn nơi phổ biến. Heck, tôi sẽ nói rằng nhiều người trong số họ đã đến để làm việc với cơ sở dữ liệu dễ dàng hơn (PowerBuilder có ai không?)
TomTom

@TomTom thực sự, tôi đồng ý với hầu hết nội dung của bạn. Tôi chỉ ngầm chạm vào khía cạnh bảo mật ở đây. Trên SO, tôi trả lời rất nhiều câu hỏi SPARQL (ngôn ngữ truy vấn RDF, với một số điểm tương đồng với SQL) và rất nhiều người gặp vấn đề vì họ nối các chuỗi thay vì sử dụng các truy vấn được tham số hóa. Ngay cả khi không có các cuộc tấn công tiêm chích, các truy vấn được tham số hóa giúp tránh các lỗi / sự cố và lỗi / sự cố cũng có thể là vấn đề bảo mật, ngay cả khi chúng không phải là các cuộc tấn công tiêm chích. Vì vậy, tôi sẽ nói ít hơn và nhiều hơn: các truy vấn được tham số hóa là tốt, ngay cả khi SQL tiêm không phải là vấn đề và chúng vẫn tốt ...
Joshua Taylor

21

Nó giúp tùy chọn # 2 thường được coi là một cách thực hành tốt nhất vì cơ sở dữ liệu có thể lưu trữ phiên bản truy vấn không được tham số hóa. Các truy vấn được tham số hóa trước vấn đề tiêm SQL vài năm (tôi tin), điều đó xảy ra đến mức bạn có thể giết chết hai con chim bằng một viên đá.


10
SQL tiêm đã là một vấn đề kể từ khi SQL lần đầu tiên được phát minh. Nó đã không trở thành một vấn đề sau này.
Phục vụ

9
@Servy Về mặt lý thuyết có. Trên thực tế, nó chỉ trở thành một vấn đề thực sự khi các cơ chế đầu vào của chúng tôi lên mạng, đưa ra một bề mặt tấn công lớn cho bất kỳ ai để búa.
Jan Doggen

8
Các bảng Bobby nhỏ sẽ không đồng ý rằng bạn cần internet hoặc cơ sở người dùng lớn để tận dụng SQL SQL. Và tất nhiên, các mạng SQL có trước ngày , vì vậy không giống như bạn sẽ phải đợi mạng sau khi SQL xuất hiện. Vâng, lỗ hổng bảo mật là ít dễ bị tổn thương khi ứng dụng của bạn có một cơ sở người dùng nhỏ, nhưng họ vẫn đang lỗ hổng bảo mật, và người ta khai thác chúng khi cơ sở dữ liệu chính nó có dữ liệu quan trọng (và nhiều rất cơ sở dữ liệu ban đầu có dữ liệu rất có giá trị, như chỉ những người với cơ sở dữ liệu có giá trị có thể chi trả cho công nghệ) ..
Phục vụ

5
Theo hiểu biết của tôi, SQL động là một tính năng tương đối muộn; Việc sử dụng SQL ban đầu chủ yếu được biên dịch trước / xử lý trước với các tham số cho các giá trị (cả trong và ngoài), vì vậy các tham số trong truy vấn có thể có trước SQL tiêm trong phần mềm (có thể không phải trong truy vấn ad-hoc / CLI).
Đánh dấu Rotteveel

6
Họ có thể biết trước về việc tiêm SQL.
user253751

20

Nói một cách đơn giản: Họ đã không làm thế. Tuyên bố của bạn:

Tại sao cơ chế ngăn chặn SQL Injection phát triển theo hướng sử dụng Truy vấn tham số?

về cơ bản là thiếu sót. Các truy vấn được tham số hóa đã tồn tại cách lâu hơn SQL Injection ít nhất được biết đến rộng rãi. Chúng thường được phát triển như một cách để tránh sự tập trung chuỗi trong chức năng "hình thức tìm kiếm" thông thường mà các ứng dụng LOB (Line of Business) có. Nhiều - NHIỀU - năm sau, ai đó đã tìm thấy một vấn đề bảo mật với thao tác chuỗi đã nói.

Tôi nhớ đã làm SQL 25 năm trước (khi internet KHÔNG được sử dụng rộng rãi - nó mới chỉ bắt đầu) và tôi nhớ đã làm SQL so với IBM DB5 IIRC phiên bản 5 - và điều đó đã có các truy vấn được tham số hóa.


cảm ơn. Tại sao cần phải tránh nối chuỗi? Dường như với tôi đó sẽ là một tính năng hữu ích. Có ai có vấn đề với nó?
Dennis

3
Hai thực sự. Đầu tiên, nó không phải lúc nào cũng hoàn toàn tầm thường - tại sao phải giải quyết việc cấp phát bộ nhớ, v.v. khi không cần thiết. Nhưng thứ hai, trong thời cổ đại, bộ nhớ cache của cơ sở dữ liệu sql không chính xác đến mức tuyệt vời - SQL biên dịch rất tốn kém. Vì tác dụng phụ của việc sử dụng một câu lệnh chuẩn bị sql (là nơi xuất phát các tham số), các kế hoạch loại trừ có thể được sử dụng lại. SQL Server đã giới thiệu tham số tự động (để sử dụng lại các kế hoạch truy vấn ngay cả khi không có tham số - chúng bị khấu trừ và ngụ ý) Tôi nghĩ rằng 2000 hoặc 2007 - ở đâu đó ở giữa, IIRC.
TomTom

2
Có các truy vấn tham số hóa không loại bỏ khả năng thực hiện nối chuỗi. Bạn có thể thực hiện nối chuỗi để tạo truy vấn được tham số hóa. Chỉ vì một tính năng hữu ích không có nghĩa là nó luôn là lựa chọn tốt cho một vấn đề nhất định.
JimmyJames

Đúng, nhưng như tôi đã nói - vào thời điểm chúng được phát minh, SQL động đã đạt được hiệu năng khá tốt;) Thậm chí ngày nay mọi người còn nói với bạn rằng các kế hoạch truy vấn SQL động trong máy chủ sql không được sử dụng lại (đó là sai vì - hm - như Tôi đã nói một số điểm giữa năm 2000 và 2007 - vì vậy QUITE dài). Vào thời điểm cũ, bạn thực sự muốn các câu lệnh CHUẨN BỊ nếu bạn chạy sql nhiều lần;)
TomTom

Kế hoạch bộ nhớ đệm cho SQL động trên thực tế đã được thêm vào SQL Server 7.0, vào năm 1998 - sqlmag.com/database-performance-tuning/ Kẻ
Mike Dimmick

13

Ngoài ra tất cả các câu trả lời tốt khác:

Lý do tại sao # 2 tốt hơn là vì nó tách dữ liệu của bạn khỏi mã của bạn. Trong số 1, dữ liệu của bạn là một phần của mã của bạn và đó là nơi tất cả những điều xấu xuất phát. Với # 1, bạn nhận được truy vấn của mình và cần thực hiện các bước bổ sung để đảm bảo truy vấn của bạn hiểu dữ liệu của bạn dưới dạng dữ liệu trong khi ở # 2 bạn nhận được mã của mình và đó là mã và dữ liệu của bạn là dữ liệu.


3
Tách mã và dữ liệu cũng có nghĩa là các biện pháp phòng vệ của bạn chống lại việc tiêm mã thù địch được viết và kiểm tra bởi nhà cung cấp cơ sở dữ liệu. Do đó, nếu một cái gì đó được truyền dưới dạng tham số cùng với truy vấn vô hại kết thúc việc chuyển đổi hoặc phá hoại cơ sở dữ liệu của bạn, danh tiếng của cơ sở dữ liệu sẽ xuất hiện và org của bạn thậm chí có thể kiện họ và giành chiến thắng. Điều đó cũng có nghĩa là nếu mã đó có lỗi có thể khai thác, tỷ lệ cược khá tốt là đó là trang web của người khác, nơi tất cả các quái vật bị phá vỡ, thay vì của bạn. (Chỉ cần bỏ qua các lỗi bảo mật!)
nigel222

11

Các truy vấn được tham số hóa, ngoài việc cung cấp bảo vệ tiêm SQL, thường có một lợi ích bổ sung là chỉ được biên dịch một lần, sau đó được thực thi nhiều lần với các tham số khác nhau.

Từ quan điểm cơ sở dữ liệu SQL select * from employees where last_name = 'Smith'select * from employees where last_name = 'Fisher'khác biệt rõ ràng và do đó yêu cầu phân tích, biên dịch và tối ưu hóa riêng biệt. Chúng cũng sẽ chiếm các vị trí riêng biệt trong vùng nhớ dành riêng cho việc lưu trữ các câu lệnh được biên dịch. Trong một hệ thống được tải nặng với một số lượng lớn các truy vấn tương tự có tính toán tham số khác nhau và chi phí bộ nhớ có thể là đáng kể.

Sau đó, sử dụng các truy vấn tham số thường cung cấp các lợi thế hiệu suất lớn.


Tôi nghĩ đó là lý thuyết (dựa trên các câu lệnh được chuẩn bị đã sử dụng cho các truy vấn được tham số hóa). Trong thực tế, tôi nghi ngờ điều này thực sự thường xảy ra, vì hầu hết các triển khai sẽ chỉ chuẩn bị ràng buộc-thực thi trong một cuộc gọi, vì vậy hãy sử dụng một câu lệnh được chuẩn bị khác nhau cho mỗi truy vấn được tham số trừ khi bạn thực hiện các bước rõ ràng để chuẩn bị các câu lệnh (và một thư viện -level preparethường khá khác so với mức SQL thực tế prepare).
jcaron

Các truy vấn sau đây cũng khác với trình phân tích cú pháp SQL: SELECT * FROM employees WHERE last_name IN (?, ?)SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Damian Yerrick

Vâng họ co. Tại sao MS lại thêm bộ nhớ đệm kế hoạch truy vấn vào năm 1998 cho SQL Server 7. Như trong: Thông tin của bạn là một thế hệ cũ.
TomTom

1
@TomTom - bộ nhớ đệm kế hoạch truy vấn không giống như tự động tham số hóa, tại đó bạn dường như đang gợi ý. Như trong, đọc trước khi bạn đăng.
mustaccio

@mustaccio Trên thực tế ít nhất MS đã giới thiệu cả hai cùng một lúc.
TomTom

5

Chờ nhưng tại sao?

Tùy chọn 1 có nghĩa là bạn phải viết các thói quen vệ sinh cho từng loại đầu vào trong khi tùy chọn 2 ít bị lỗi hơn và ít mã hơn để bạn viết / kiểm tra / bảo trì.

Hầu như chắc chắn "chăm sóc tất cả các cảnh báo" có thể phức tạp hơn bạn nghĩ và ngôn ngữ của bạn (ví dụ Java PreparedStatement) có nhiều thứ hơn bạn nghĩ.

Các câu lệnh được chuẩn bị hoặc các truy vấn tham số được biên dịch sẵn trong máy chủ cơ sở dữ liệu, do đó, khi các tham số được đặt, không có phép nối SQL nào được thực hiện do truy vấn không còn là chuỗi SQL. Một lợi thế về mặt quảng cáo là RDBMS lưu trữ truy vấn và các cuộc gọi tiếp theo được coi là cùng một SQL ngay cả khi các giá trị tham số khác nhau, trong khi với SQL được nối mỗi khi truy vấn được chạy với các giá trị khác nhau thì truy vấn khác nhau và RDBMS phải phân tích cú pháp , tạo lại kế hoạch thực hiện, v.v.


1
JDBC không vệ sinh anithing. Giao thức có một phần cụ thể cho tham số và DB đơn giản không diễn giải các tham số đó. Đó là lý do tại sao bạn có thể đặt tên bảng từ tham số.
Talex

1
Tại sao? nếu tham số không được phân tích cú pháp hoặc giải thích thì không có lý do gì để thoát khỏi cái gì đó.
Talex

11
Tôi nghĩ rằng bạn có hình ảnh sai về cách một truy vấn tham số hoạt động. Nó không chỉ là một trường hợp của các tham số được thay thế sau này, chúng không bao giờ được thay thế trong . DBMS biến bất kỳ truy vấn nào thành một "kế hoạch", một tập hợp các bước sẽ thực hiện để có kết quả của bạn; trong một truy vấn được tham số hóa, kế hoạch đó giống như một hàm: nó có một số biến cần được cung cấp khi nó được thực thi. Vào thời điểm các biến được cung cấp, chuỗi SQL đã hoàn toàn bị lãng quên và kế hoạch chỉ được thực hiện với các giá trị được cung cấp.
IMSoP

2
@IMSoP Đó là một quan niệm sai lầm của tôi. Mặc dù tôi nghĩ đó là một câu hỏi phổ biến như bạn có thể thấy trong hai câu trả lời được bình chọn nhiều nhất cho câu hỏi này trong SO stackoverflow.com/questions/3271249/ . Tôi đọc về nó và bạn đã đúng. Tôi chỉnh sửa câu trả lời.
Tulains Córdova

3
@TomTom Điều đó thật tuyệt vời cho hiệu suất , nhưng nó không làm gì cho bảo mật . Vào thời điểm một đoạn SQL động bị xâm phạm được biên dịch và lưu vào bộ đệm, chương trình đã bị thay đổi . Tạo một kế hoạch từ SQL được tham số hóa không động và sau đó chuyển các phần tử dữ liệu về cơ bản vẫn khác với DBMS trừu tượng hóa sự giống nhau giữa hai truy vấn được trình bày dưới dạng chuỗi SQL hoàn chỉnh.
IMSoP

1

Chúng ta hãy tưởng tượng một cách tiếp cận "vệ sinh, lọc và mã hóa" lý tưởng sẽ như thế nào.

Vệ sinh và lọc có thể có ý nghĩa trong ngữ cảnh của một ứng dụng cụ thể, nhưng cuối cùng cả hai đều sôi sục khi nói rằng "bạn không thể đưa dữ liệu này vào cơ sở dữ liệu". Đối với ứng dụng của bạn, đó có thể là một ý tưởng tốt, nhưng đó không phải là thứ bạn có thể đề xuất như một giải pháp chung, vì sẽ có những ứng dụng cần có khả năng lưu trữ các ký tự tùy ý trong cơ sở dữ liệu.

Vì vậy mà lá mã hóa. Bạn có thể bắt đầu bằng cách có một chức năng mã hóa chuỗi bằng cách thêm các ký tự thoát, để bạn có thể thay thế chúng trong chính mình. Vì các cơ sở dữ liệu khác nhau cần các ký tự thoát khác nhau (trong một số cơ sở dữ liệu, cả hai \'''là các chuỗi thoát hợp lệ cho ', nhưng không phải trong các cơ sở dữ liệu khác), nên chức năng này cần được cung cấp bởi nhà cung cấp cơ sở dữ liệu.

Nhưng không phải tất cả các biến là chuỗi. Đôi khi bạn cần thay thế bằng một số nguyên hoặc một ngày. Chúng được biểu diễn khác nhau cho các chuỗi, vì vậy bạn cần các phương thức mã hóa khác nhau (một lần nữa, chúng cần phải cụ thể cho nhà cung cấp cơ sở dữ liệu) và bạn cần thay thế chúng vào truy vấn theo các cách khác nhau.

Vì vậy, có thể mọi thứ sẽ dễ dàng hơn nếu cơ sở dữ liệu xử lý thay thế cho bạn - nó đã biết loại truy vấn mong đợi và cách mã hóa dữ liệu an toàn và cách thay thế chúng vào truy vấn của bạn một cách an toàn, vì vậy bạn không cần phải lo lắng về nó trong mã của bạn.

Tại thời điểm này, chúng tôi vừa phát minh lại các truy vấn được tham số hóa.

Và một khi các truy vấn được tham số hóa, nó sẽ mở ra các cơ hội mới, chẳng hạn như tối ưu hóa hiệu suất và giám sát đơn giản hóa.

Mã hóa là khó thực hiện đúng và mã hóa-thực hiện-quyền không thể phân biệt với tham số hóa.

Nếu bạn thực sự thích nội suy chuỗi như một cách xây dựng truy vấn, có một số ngôn ngữ (Scala và ES2015 xuất hiện) có nội suy chuỗi có thể cắm, do đó, những thư viện cho phép bạn viết các truy vấn được tham số hóa trông giống như nội suy chuỗi, nhưng an toàn khỏi SQL tiêm - vì vậy trong cú pháp ES2015:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

1
"Mã hóa là khó để làm đúng" - hahaha. Không phải vậy. Một hoặc hai ngày, tất cả là tài liệu. Tôi đã viết một bộ mã hóa từ nhiều năm trước cho một ORM (vì máy chủ sql có giới hạn về tham số và do đó rất khó để chèn 5000-10000 hàng trong một câu lệnh (trở lại 15 năm trước). Tôi không nhớ đó là một vấn đề lớn.
TomTom

1
Có lẽ SQL Server đủ thường xuyên rằng đó không phải là vấn đề, nhưng tôi đã gặp phải sự cố trong các DB khác - các trường hợp góc với mã hóa ký tự không khớp, tùy chọn cấu hình tối nghĩa, các vấn đề về ngày và số cụ thể. Tất cả đều có thể giải quyết được, nhưng cần ít nhất một sự hiểu biết khó hiểu về những điều kỳ quặc của DB (Tôi đang nhìn vào bạn, MySQL và Oracle).
James_pic

3
@TomTom Mã hóa thực sự rất khó để có được ngay khi bạn tính đến thời gian. Bạn làm gì khi nhà cung cấp DB của bạn quyết định tạo một kiểu nhận xét mới trong phiên bản tiếp theo hoặc khi một bareword trở thành một từ khóa mới trong bản nâng cấp? Về mặt lý thuyết, bạn có thể nhận được mã hóa thực sự đúng cho một bản phát hành RDBMS của bạn và sai trong lần sửa đổi tiếp theo. Thậm chí đừng bắt đầu với những gì xảy ra khi bạn chuyển nhà cung cấp sang một người có nhận xét
Eric

@Eric, thật là kinh khủng. (Tôi sử dụng Postgres; nếu nó có bất kỳ mụn cóc kỳ quái nào như vậy tôi vẫn chưa gặp phải.)
Wildcard

0

Trong tùy chọn 1, bạn đang làm việc với một bộ đầu vào có kích thước = vô cực mà bạn đang cố ánh xạ tới kích thước đầu ra rất lớn. Trong tùy chọn 2, bạn đã giới hạn đầu vào của mình cho bất cứ điều gì bạn chọn. Nói cách khác:

  1. Sàng lọc và lọc cẩn thận [ vô cực ] cho [ tất cả các truy vấn SQL an toàn ]
  2. Sử dụng [ kịch bản được xem xét trước giới hạn trong phạm vi của bạn ]

Theo các câu trả lời khác, dường như cũng có một số lợi ích về hiệu suất từ ​​việc giới hạn phạm vi của bạn khỏi vô cực và hướng tới một cái gì đó có thể quản lý được.


0

Một mô hình tinh thần hữu ích của SQL (đặc biệt là phương ngữ hiện đại) là mỗi câu lệnh hoặc truy vấn SQL là một chương trình. Trong một chương trình thực thi nhị phân riêng, các loại lỗ hổng bảo mật nguy hiểm nhất sẽ tràn ra nơi kẻ tấn công có thể ghi đè hoặc sửa đổi mã chương trình bằng các hướng dẫn khác nhau.

Lỗ hổng SQL tiêm là đẳng cấu với tràn bộ đệm trong ngôn ngữ như C. Lịch sử đã chỉ ra rằng tràn bộ đệm là cực kỳ khó ngăn chặn - ngay cả đối tượng mã cực kỳ quan trọng để xem xét mở thường chứa các lỗ hổng như vậy.

Một khía cạnh quan trọng của phương pháp hiện đại để giải quyết các lỗ hổng tràn là sử dụng các cơ chế phần cứng và hệ điều hành để đánh dấu các phần cụ thể của bộ nhớ là không thể thực thi và đánh dấu các phần khác của bộ nhớ là chỉ đọc. (Xem bài viết Wikipedia về bảo vệ không gian thực thi , chẳng hạn.) Theo cách đó, ngay cả khi kẻ tấn công có thể sửa đổi dữ liệu, kẻ tấn công không thể khiến dữ liệu được tiêm của chúng được coi là mã.

Vì vậy, nếu lỗ hổng SQL tiêm tương đương với lỗi tràn bộ đệm, thì SQL tương đương với bit NX hay các trang bộ nhớ chỉ đọc là gì? Câu trả lời là: các câu lệnh được chuẩn bị , bao gồm các truy vấn được tham số hóa cộng với các cơ chế tương tự cho các yêu cầu không truy vấn. Câu lệnh đã chuẩn bị được biên dịch với một số phần nhất định được đánh dấu chỉ đọc, vì vậy kẻ tấn công không thể thay đổi các phần đó của chương trình và các phần khác được đánh dấu là dữ liệu không thể thực thi (các tham số của câu lệnh đã chuẩn bị) mà kẻ tấn công có thể đưa dữ liệu vào nhưng mà sẽ không bao giờ được coi là mã chương trình, do đó loại bỏ hầu hết các khả năng lạm dụng.

Chắc chắn, vệ sinh đầu vào của người dùng là tốt, nhưng để thực sự an toàn, bạn cần phải hoang tưởng (hoặc, tương đương, để suy nghĩ như một kẻ tấn công). Bề mặt điều khiển bên ngoài văn bản chương trình là cách để thực hiện điều đó và các câu lệnh được chuẩn bị cung cấp bề mặt điều khiển đó cho SQL. Vì vậy, không có gì ngạc nhiên khi các câu lệnh được chuẩn bị, và do đó các truy vấn được tham số hóa, là cách tiếp cận mà đại đa số các chuyên gia bảo mật khuyên dùng.


Đây là tất cả tốt đẹp và bảnh bao, nhưng nó không giải quyết câu hỏi theo tiêu đề nào cả.
TomTom

1
@TomTom: Ý bạn là gì? Câu hỏi chính xác là tại sao các truy vấn được tham số hóa là cơ chế ưa thích để ngăn chặn việc tiêm SQL; câu trả lời của tôi giải thích tại sao các truy vấn được tham số hóa an toàn và mạnh mẽ hơn vệ sinh đầu vào của người dùng.
Daniel Pryden

Tôi xin lỗi, nhưng câu hỏi của tôi có nội dung "Tại sao cơ chế ngăn chặn SQL Injection phát triển theo hướng sử dụng Truy vấn tham số?". Họ đã không. Nó không phải là về bây giờ, nó là về lịch sử.
TomTom

0

Tôi alredy viết về điều này ở đây: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

Nhưng, chỉ để giữ cho nó đơn giản:

Cách các truy vấn được tham số hóa hoạt động, là sqlQuery được gửi dưới dạng truy vấn và cơ sở dữ liệu biết chính xác truy vấn này sẽ làm gì và chỉ sau đó nó mới chèn tên người dùng và mật khẩu làm giá trị. Điều này có nghĩa là họ không thể thực hiện truy vấn, vì cơ sở dữ liệu đã biết truy vấn sẽ làm gì. Vì vậy, trong trường hợp này, nó sẽ tìm tên người dùng "Không ai HOẶC 1 = 1 '-" và mật khẩu trống, sẽ xuất hiện sai.

Tuy nhiên, đây không phải là một giải pháp hoàn chỉnh và việc xác thực đầu vào vẫn cần phải được thực hiện, vì điều này sẽ không ảnh hưởng đến các vấn đề khác, như các cuộc tấn công XSS, vì bạn vẫn có thể đưa javascript vào cơ sở dữ liệu. Sau đó, nếu điều này được đọc ra trên một trang, nó sẽ hiển thị nó dưới dạng javascript bình thường, tùy thuộc vào bất kỳ xác thực đầu ra nào. Vì vậy, điều thực sự tốt nhất vẫn là sử dụng xác thực đầu vào, nhưng sử dụng các truy vấn được tham số hóa hoặc các thủ tục được lưu trữ để ngăn chặn mọi cuộc tấn công SQL


0

Tôi chưa bao giờ sử dụng SQL. Nhưng rõ ràng bạn nghe về những vấn đề mà mọi người gặp phải và các nhà phát triển SQL gặp vấn đề với điều "SQL tiêm" này. Trong một thời gian dài, tôi không thể tìm ra nó. Và sau đó tôi nhận ra rằng những người tạo ra các câu lệnh SQL, các câu lệnh nguồn SQL văn bản thực sự, bằng cách nối các chuỗi, trong đó một số nơi được người dùng nhập vào. Và suy nghĩ đầu tiên của tôi về nhận thức đó là sốc. Tổng sốc. Tôi nghĩ: Làm thế nào có ai có thể ngu ngốc đến thế và tạo ra các câu lệnh trong bất kỳ ngôn ngữ lập trình nào như vậy? Đối với một nhà phát triển C, hoặc C ++ hoặc Java hoặc Swift, đây là điều hoàn toàn điên rồ.

Điều đó nói rằng, không khó để viết một hàm C lấy một chuỗi C làm đối số của nó và tạo ra một chuỗi khác trông giống hệt như một chuỗi ký tự trong mã nguồn C đại diện cho cùng một chuỗi. Ví dụ: hàm đó sẽ dịch abc thành "abc" và "abc" thành "\" abc \ "" và "\" abc \ "" thành "\" \\ "abc \\" \ "". (Chà, nếu điều này có vẻ sai với bạn, đó là html. Nó đúng khi tôi nhập nó vào, nhưng không phải khi nó được hiển thị) Và một khi chức năng C được viết, việc tạo mã nguồn C không khó khăn gì cả văn bản từ trường đầu vào do người dùng cung cấp được chuyển thành chuỗi C bằng chữ. Điều đó không khó để làm cho an toàn. Tại sao các nhà phát triển SQL sẽ không sử dụng cách tiếp cận đó như một cách để tránh việc tiêm SQL nằm ngoài tôi.

"Vệ sinh" là một cách tiếp cận hoàn toàn sai lầm. Lỗ hổng nghiêm trọng là nó làm cho một số người dùng đầu vào bất hợp pháp. Bạn kết thúc với một cơ sở dữ liệu trong đó một trường văn bản chung không thể chứa văn bản như; Thả bảng hoặc bất cứ điều gì bạn sẽ sử dụng trong một SQL tiêm để gây ra thiệt hại. Tôi thấy điều đó khá khó chấp nhận. Nếu một cơ sở dữ liệu lưu trữ văn bản, nó sẽ có thể lưu trữ bất kỳ văn bản nào . Và lỗ hổng thực tế là chất khử trùng dường như không thể hiểu đúng :-(

Tất nhiên, các truy vấn được tham số hóa là điều mà bất kỳ lập trình viên nào sử dụng ngôn ngữ biên dịch sẽ mong đợi. Nó làm cho cuộc sống dễ dàng hơn nhiều: Bạn có một số đầu vào chuỗi và thậm chí bạn không bao giờ bận tâm dịch nó thành một chuỗi SQL, mà chỉ chuyển nó dưới dạng tham số, không có bất kỳ ký tự nào trong chuỗi đó gây ra bất kỳ thiệt hại nào.

Vì vậy, từ quan điểm của một nhà phát triển sử dụng các ngôn ngữ được biên dịch, vệ sinh là điều sẽ không bao giờ xảy ra với tôi. Nhu cầu vệ sinh là điên rồ. Các truy vấn tham số là giải pháp rõ ràng cho vấn đề.

(Tôi thấy câu trả lời của Josip rất thú vị. Về cơ bản, ông nói rằng với các truy vấn được tham số hóa, bạn có thể ngăn chặn mọi cuộc tấn công chống lại SQL, nhưng sau đó bạn có thể có văn bản trong cơ sở dữ liệu của mình được sử dụng để tạo ra một mũi tiêm JavaScript :-( Chà, chúng ta lại gặp vấn đề tương tự và tôi không biết liệu Javascript có giải pháp cho vấn đề đó không.


-2

Vấn đề chính là tin tặc đã tìm ra cách để bao quanh vệ sinh trong khi các truy vấn được tham số hóa là một quy trình hiện có hoạt động hoàn hảo với các lợi ích bổ sung về hiệu suất và bộ nhớ.

Một số người đơn giản hóa vấn đề là "đó chỉ là trích dẫn đơn và trích dẫn kép" nhưng tin tặc đã tìm ra những cách thông minh để tránh bị phát hiện như sử dụng các mã hóa khác nhau hoặc sử dụng các chức năng cơ sở dữ liệu.

Dù sao, bạn chỉ cần quên một chuỗi duy nhất để tạo ra một vi phạm dữ liệu thảm khốc. Tin tặc nơi có thể tự động hóa các tập lệnh để tải xuống cơ sở dữ liệu hoàn chỉnh với một loạt hoặc truy vấn. Nếu phần mềm nổi tiếng như một bộ mã nguồn mở hoặc một bộ kinh doanh nổi tiếng, bạn có thể chỉ cần sử dụng bảng mật khẩu và người dùng.

Mặt khác, chỉ sử dụng các truy vấn được kết nối chỉ là vấn đề học cách sử dụng và làm quen với 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.