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?
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?
Câu trả lời:
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ị.
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.
Để 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,
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.
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.
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.
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.
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.
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.
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
UPDATE người dùng đặt tên người dùng =? và mật khẩu =? WHERE id =?
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.
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.
(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
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.
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?
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 user
tham 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.
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/
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.
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ị.
Như đã giải thích trong bài đăng này , PreparedStatement
mộ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:
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 đó:
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