Làm cách nào để PreparedStatement tránh hoặc ngăn chặn việc đưa SQL vào?


122

Tôi biết rằng PreparedStatements tránh / ngăn chặn SQL Injection. sao làm được vậy? Truy vấn biểu mẫu cuối cùng được tạo bằng PreparedStatements sẽ là một chuỗi hay cách khác?


3
Về mặt kỹ thuật, thông số JDBC không nhấn mạnh rằng không có lỗi chèn SQL. Tôi không biết có ổ nào bị ảnh hưởng.
Tom Hawtin - tackline 17/10/09

3
@Jayesh Tôi khuyên bạn nên thêm nội dung blog của bạn làm câu trả lời ở đây. Hầu hết các câu trả lời chỉ là cho biết sự khác biệt giữa tạo truy vấn SQL động b / w và stmt đã chuẩn bị. Họ không giải quyết vấn đề tại sao các tuyên bố chuẩn bị hoạt động tốt hơn so với blog của bạn.
Pavan Manjunath

1
Được thêm vào dưới dạng câu trả lời, tôi hy vọng nó sẽ hữu ích.
Jayesh

Câu trả lời:


78

Vấn đề với SQL injection là đầu vào của người dùng được sử dụng như một phần của câu lệnh SQL. Bằng cách sử dụng các câu lệnh đã chuẩn bị, bạn có thể buộc xử lý đầu vào của người dùng dưới dạng nội dung của một tham số (chứ không phải là một phần của lệnh SQL).

Nhưng nếu bạn không sử dụng đầu vào của người dùng làm tham số cho câu lệnh đã chuẩn bị của mình mà thay vào đó xây dựng lệnh SQL bằng cách nối các chuỗi lại với nhau, bạn vẫn dễ bị chèn SQL ngay cả khi sử dụng câu lệnh đã chuẩn bị.


1
Chắc chắn rồi, nhưng bạn vẫn có thể mã hóa cứng một số hoặc tất cả các tham số của mình.
tangens

16
Vui lòng làm ví dụ - Nhưng nếu bạn không sử dụng đầu vào của người dùng làm tham số cho câu lệnh đã chuẩn bị của mình mà thay vào đó xây dựng lệnh SQL của bạn bằng cách nối các chuỗi lại với nhau, bạn vẫn dễ bị chèn SQL ngay cả khi sử dụng câu lệnh đã chuẩn bị.
david blaine

4
FWIW Các câu lệnh được chuẩn bị sẵn không phải là một thứ JDBC - chúng là một thứ SQL. Bạn có thể chuẩn bị và thực thi các câu lệnh đã chuẩn bị từ bên trong bảng điều khiển SQL. PreparedStatement chỉ hỗ trợ chúng từ bên trong JDBC.
beldaz 25/09/13

198

Hãy xem xét hai cách để làm điều tương tự:

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

Hoặc là

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

Nếu "người dùng" đến từ đầu vào của người dùng và đầu vào của người dùng là

Robert'); DROP TABLE students; --

Sau đó, trong trường hợp đầu tiên, bạn sẽ được đào. Trong lần thứ hai, bạn sẽ được an toàn và Little Bobby Tables sẽ được đăng ký cho trường học của bạn.


8
Vì vậy, nếu tôi hiểu đúng, truy vấn trong ví dụ thứ hai sẽ được thực thi sẽ thực sự là: CHÈN VÀO GIÁ TRỊ của sinh viên ("Robert '); DROP TABLE sinh viên; -") - hoặc ít nhất là một cái gì đó tương tự. Điều này có đúng không?
Tối đa

18
Không, trong trường hợp ĐẦU TIÊN, bạn sẽ nhận được tuyên bố đó. Trong cái thứ hai, nó sẽ chèn sinh viên "Robert '); DROP TABLE; -" vào bảng người dùng.
Paul Tomblin

3
Đó là ý tôi, trong ví dụ thứ hai (ví dụ "an toàn"), chuỗi Robert '); Sinh viên DROP TABLE; - sẽ được lưu vào trường trong bảng sinh viên. Tôi đã viết một cái gì đó khác? ;)
Tối đa

7
Xin lỗi, các dấu ngoặc kép lồng vào nhau là điều tôi cố gắng tránh vì sự nhầm lẫn như thế này. Đó là lý do tại sao tôi thích PreparedStatements với các tham số.
Paul Tomblin

59
Bàn Bobby Nhỏ. XD Tham khảo tuyệt vời
Amalgovinus

129

Để hiểu cách PreparedStatement ngăn chặn SQL Injection, chúng ta cần hiểu các giai đoạn thực thi SQL Query.

1. Giai đoạn Biên dịch. 2. Giai đoạn Thực hiện.

Bất cứ khi nào công cụ máy chủ SQL nhận được một truy vấn, nó phải chuyển qua các giai đoạn dưới đây,

Các giai đoạn thực thi truy vấn

  1. Giai đoạn phân tích cú pháp và chuẩn hóa: Trong giai đoạn này, Truy vấn được kiểm tra cú pháp và ngữ nghĩa. Nó kiểm tra xem bảng tham chiếu và các cột được sử dụng trong truy vấn có tồn tại hay không. Nó cũng có nhiều nhiệm vụ khác phải làm, nhưng chúng ta hãy không đi vào chi tiết.

  2. Giai đoạn Biên dịch: Trong giai đoạn này, các từ khóa được sử dụng trong truy vấn như select, from, where, v.v. được chuyển đổi thành định dạng có thể hiểu được bằng máy. Đây là giai đoạn mà truy vấn được diễn giải và quyết định hành động tương ứng sẽ thực hiện. Nó cũng có nhiều nhiệm vụ khác phải làm, nhưng chúng ta hãy không đi vào chi tiết.

  3. Kế hoạch tối ưu hóa truy vấn: Trong giai đoạn này, Cây quyết định được tạo để tìm các cách có thể thực hiện truy vấn. Nó tìm ra số cách mà truy vấn có thể được thực hiện và chi phí liên quan đến mỗi cách thực hiện truy vấn. Nó chọn phương án tốt nhất để thực hiện một truy vấn.

  4. Bộ nhớ đệm : Kế hoạch tốt nhất được chọn trong Kế hoạch tối ưu hóa truy vấn được lưu trữ trong bộ nhớ cache, để bất cứ khi nào có cùng một truy vấn tiếp theo, nó không phải chuyển qua Giai đoạn 1, Giai đoạn 2 và Giai đoạn 3 một lần nữa. Khi truy vấn lần sau xuất hiện, nó sẽ được kiểm tra trực tiếp trong Cache và được chọn từ đó để thực thi.

  5. Giai đoạn Thực thi: Trong giai đoạn này, truy vấn được cung cấp sẽ được thực thi và dữ liệu được trả lại cho người dùng dưới dạng ResultSetđối tượng.

Hành vi của API PreparedStatement ở các bước trên

  1. PreparedStatements không phải là truy vấn SQL hoàn chỉnh và chứa (các) trình giữ chỗ, tại thời điểm chạy được thay thế bằng dữ liệu thực tế do người dùng cung cấp.

  2. Bất cứ khi nào bất kỳ PreparedStatment nào có chứa trình giữ chỗ được chuyển vào công cụ SQL Server, nó sẽ chuyển qua các giai đoạn dưới đây

    1. Giai đoạn phân tích cú pháp và chuẩn hóa
    2. Giai đoạn tổng hợp
    3. Kế hoạch tối ưu hóa truy vấn
    4. Bộ nhớ cache (Truy vấn đã biên dịch với trình giữ chỗ được lưu trữ trong Cache.)

UPDATE người dùng đặt tên người dùng =? và mật khẩu =? WHERE id =?

  1. Truy vấn ở trên sẽ được phân tích cú pháp, biên dịch với trình giữ chỗ dưới dạng xử lý đặc biệt, được tối ưu hóa và được lưu vào bộ nhớ cache. Truy vấn ở giai đoạn này đã được biên dịch và chuyển đổi ở định dạng máy có thể hiểu được. Vì vậy, chúng ta có thể nói rằng Truy vấn được lưu trữ trong bộ nhớ cache được Biên dịch sẵn và chỉ các trình giữ chỗ cần được thay thế bằng dữ liệu do người dùng cung cấp.

  2. Giờ đây, tại thời điểm chạy khi dữ liệu do người dùng cung cấp đến, Truy vấn biên dịch trước được chọn từ Bộ nhớ đệm và trình giữ chỗ được thay thế bằng dữ liệu do người dùng cung cấp.

Chuẩn bị

(Hãy nhớ rằng, sau khi các trình giữ chỗ được thay thế bằng dữ liệu người dùng, truy vấn cuối cùng sẽ không được biên dịch / thông dịch lại và công cụ SQL Server xử lý dữ liệu người dùng là dữ liệu thuần túy chứ không phải là SQL cần được phân tích cú pháp hoặc biên dịch lại; đó là vẻ đẹp của PreparedStatement. )

Nếu truy vấn không phải trải qua giai đoạn biên dịch lại, thì bất kỳ dữ liệu nào được thay thế trên trình giữ chỗ đều được coi là dữ liệu thuần túy và không có ý nghĩa đối với công cụ SQL Server và nó trực tiếp thực thi truy vấn.

Lưu ý: Đây là giai đoạn biên dịch sau giai đoạn phân tích cú pháp, nó hiểu / diễn giải cấu trúc truy vấn và đưa ra hành vi có ý nghĩa cho nó. Trong trường hợp PreparedStatement, truy vấn chỉ được biên dịch một lần và truy vấn đã biên dịch được lưu trong bộ nhớ cache được chọn mọi lúc để thay thế dữ liệu người dùng và thực thi.

Do tính năng biên dịch một lần của PreparedStatement, nó không bị tấn công SQL Injection.

Bạn có thể xem giải thích chi tiết với ví dụ tại đây: https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html


3
lời giải thích hay
Dheeraj Joshi

4
Theo nghĩa đen câu trả lời đầy đủ nhất về cách thức hoạt động mảnh
jouell

Nó rất hữu ích. Cảm ơn vì lời giải thích chi tiết.
Không xác định

26

SQL được sử dụng trong một PreparedStatement được biên dịch trước trên trình điều khiển. Từ thời điểm đó, các tham số được gửi đến trình điều khiển dưới dạng giá trị theo nghĩa đen và không phải là phần thực thi của SQL; do đó không có SQL nào có thể được đưa vào bằng tham số. Một tác dụng phụ có lợi khác của PreparedStatements (biên dịch trước + chỉ gửi tham số) là cải thiện hiệu suất khi chạy câu lệnh nhiều lần ngay cả với các giá trị khác nhau cho các tham số (giả sử rằng trình điều khiển hỗ trợ PreparedStatements) vì trình điều khiển không phải thực hiện phân tích cú pháp SQL và biên dịch từng thời gian các thông số thay đổi.


Nó không cần phải được thực hiện như vậy, và tôi tin rằng nó thường không phải vậy.
Tom Hawtin - tackline 17/10/09

4
Trên thực tế, SQL thường được biên dịch trước trên cơ sở dữ liệu. Đó là, một kế hoạch thực thi được chuẩn bị trên cơ sở dữ liệu. Khi bạn thực hiện truy vấn, kế hoạch được thực thi với các tham số đó. Lợi ích bổ sung là cùng một câu lệnh có thể được thực thi với các tham số khác nhau mà bộ xử lý truy vấn không phải biên dịch một kế hoạch mới mỗi lần.
beldaz

3

Tôi đoán nó sẽ là một chuỗi. Nhưng các tham số đầu vào sẽ được gửi đến cơ sở dữ liệu và phép truyền / chuyển đổi thích hợp sẽ được áp dụng trước khi tạo một câu lệnh SQL thực.

Để cung cấp cho bạn một ví dụ, nó có thể thử và xem liệu CAST / Chuyển đổi có hoạt động hay không.
Nếu nó hoạt động, nó có thể tạo ra một tuyên bố cuối cùng từ nó.

   SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))

Hãy thử một ví dụ với câu lệnh SQL chấp nhận một tham số số.
Bây giờ, hãy thử chuyển một biến chuỗi (với nội dung số được chấp nhận làm tham số số). Nó có phát sinh bất kỳ lỗi nào không?

Bây giờ, hãy thử chuyển một biến chuỗi (với nội dung không được chấp nhận dưới dạng tham số số). Hãy xem điều gì xảy ra?


3

Tuyên bố chuẩn bị an toàn hơn. Nó sẽ chuyển đổi một tham số thành kiểu được chỉ định.

Ví dụ stmt.setString(1, user);sẽ chuyển đổi usertham số thành Chuỗi.

Giả sử rằng tham số chứa một chuỗi SQL chứa một lệnh thực thi : việc sử dụng một câu lệnh chuẩn bị sẽ không cho phép điều đó.

Nó thêm siêu ký tự (hay còn gọi là chuyển đổi tự động) vào đó.

Điều này làm cho nó an toàn hơn.


2

SQL injection: khi người dùng có cơ hội nhập thứ gì đó có thể là một phần của câu lệnh sql

Ví dụ:

Truy vấn chuỗi = “CHÈN VÀO GIÁ TRỊ của sinh viên ('” + người dùng + “')”

khi người dùng nhập “Robert '); Học sinh DROP TABLE; - ”là đầu vào, nó gây ra SQL injection

Làm thế nào tuyên bố chuẩn bị ngăn chặn điều này?

Truy vấn chuỗi = “CHÈN VÀO GIÁ TRỊ của sinh viên ('” + “: name” + “')”

tham số.addValue (“tên”, người dùng);

=> khi người dùng nhập lại “Robert '); Học sinh DROP TABLE; - “, chuỗi đầu vào được biên dịch trước trên trình điều khiển dưới dạng các giá trị theo nghĩa đen và tôi đoán nó có thể được ép kiểu như:

CAST ('Robert'); Học sinh DROP TABLE; - 'AS varchar (30))

Vì vậy, ở phần cuối, chuỗi sẽ được chèn theo đúng nghĩa đen làm tên cho bảng.

http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/


1
Nếu tôi không nhầm, Phần CAST(‘Robert’);từ CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))sẽ gãy, sau đó nó sẽ tiến hành bỏ bảng nếu trường hợp đó xảy ra. Nó không ngừng tiêm, vì vậy tôi tin rằng ví dụ này không đủ hoàn chỉnh để giải thích kịch bản.
Héctor Álvarez

1

Chuẩn bị sẵn sàng:

1) Biên dịch trước và bộ nhớ đệm phía DB của câu lệnh SQL dẫn đến việc thực thi tổng thể nhanh hơn và khả năng sử dụng lại cùng một câu lệnh SQL theo lô.

2) Tự động ngăn chặn các cuộc tấn công chèn SQL bằng cách thoát nội trang của dấu ngoặc kép và các ký tự đặc biệt khác. Lưu ý rằng điều này yêu cầu bạn sử dụng bất kỳ phương thức PreparedStatement setXxx () nào để đặt giá trị.


1

Như đã giải thích trong bài đăng này , PreparedStatementmột mình không giúp được gì nếu bạn vẫn đang nối các Chuỗi.

Ví dụ: một kẻ tấn công giả mạo vẫn có thể làm những việc sau:

  • gọi một hàm ngủ để tất cả các kết nối cơ sở dữ liệu của bạn sẽ bận, do đó làm cho ứng dụng của bạn không khả dụng
  • trích xuất dữ liệu nhạy cảm từ DB
  • bỏ qua xác thực người dùng

Không chỉ SQL, mà ngay cả JPQL hoặc HQL cũng có thể bị xâm phạm nếu bạn không sử dụng các tham số ràng buộc.

Tóm lại, bạn không bao giờ nên sử dụng nối chuỗi khi xây dựng câu lệnh SQL. Sử dụng một API chuyên dụng cho mục đích đó:


1
Cảm ơn bạn đã chỉ ra tầm quan trọng của việc sử dụng liên kết tham số, thay vì chỉ PreparedStatement. Tuy nhiên, câu trả lời của bạn dường như ngụ ý rằng việc sử dụng một API chuyên dụng là cần thiết để bảo vệ khỏi việc đưa vào SQL. Vì đây không phải là trường hợp, và việc sử dụng PreparedStatement với ràng buộc tham số cũng hoạt động, bạn có quan tâm đến việc định dạng lại không?
Wild Pottok

-3

Trong Câu lệnh chuẩn bị, người dùng buộc phải nhập dữ liệu dưới dạng tham số. Nếu người dùng nhập một số câu lệnh dễ bị tấn công như DROP TABLE hoặc SELECT * FROM USERS thì dữ liệu sẽ không bị ảnh hưởng vì chúng sẽ được coi là các tham số của câu lệnh SQL


Câu trả lời giống với câu trả lời đã chọn với ít sự lựa chọn hơn.
Julien Maret
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.