it-swarm-vi.com

Các báo cáo được chuẩn bị có an toàn 100% trước SQL tiêm không?

Các câu lệnh đã được chuẩn bị thực tế 100% an toàn trước SQL SQL, giả sử tất cả các tham số do người dùng cung cấp được truyền dưới dạng tham số ràng buộc truy vấn?

Bất cứ khi nào tôi thấy mọi người sử dụng các hàm mysql_ Cũ trên StackOverflow (thật đáng buồn là cách quá thường xuyên) tôi thường nói với mọi người rằng các câu lệnh được chuẩn bị là Chuck Norris (hoặc Jon Skeet) của các biện pháp bảo mật tiêm SQL.

Tuy nhiên, tôi chưa bao giờ thực sự thấy bất kỳ tài liệu nào nói rõ "cái này an toàn 100%". Sự hiểu biết của tôi về chúng là chúng tách biệt ngôn ngữ truy vấn và các tham số đến tận cửa trước của máy chủ, sau đó coi chúng là các thực thể riêng biệt.

Tôi có đúng trong giả định này không?

80
Polynomial

Đảm bảo 100% an toàn từ SQL tiêm? Sẽ không có được nó (từ tôi).

Về nguyên tắc, cơ sở dữ liệu của bạn (hoặc thư viện trong ngôn ngữ của bạn đang tương tác với db) có thể triển khai các câu lệnh được chuẩn bị với các tham số bị ràng buộc theo cách không an toàn dễ bị tấn công nâng cao, giả sử khai thác bộ đệm tràn hoặc có các ký tự kết thúc null trong người dùng- cung cấp các chuỗi, v.v. (Bạn có thể lập luận rằng các loại tấn công này không nên được gọi là SQL tiêm vì chúng khác nhau về cơ bản; nhưng đó chỉ là ngữ nghĩa).

Tôi chưa bao giờ nghe thấy bất kỳ cuộc tấn công nào trong số các cuộc tấn công vào các câu lệnh được chuẩn bị trên cơ sở dữ liệu thực tế trong trường và đề nghị mạnh mẽ sử dụng các tham số ràng buộc để ngăn chặn việc tiêm SQL. Không có các tham số ràng buộc hoặc vệ sinh đầu vào, việc thực hiện SQL SQL không quan trọng. Chỉ với vệ sinh đầu vào, khá thường xuyên có thể tìm thấy một lỗ hổng tối nghĩa xung quanh vệ sinh.

Với các tham số bị ràng buộc, kế hoạch thực hiện truy vấn SQL của bạn được tìm ra trước thời hạn mà không phụ thuộc vào đầu vào của người dùng, điều này sẽ khiến cho việc tiêm SQL không thể thực hiện được (vì mọi trích dẫn được chèn, ký hiệu nhận xét, v.v. chỉ được chèn trong câu lệnh SQL đã biên dịch).

Đối số duy nhất chống lại việc sử dụng các câu lệnh được chuẩn bị là bạn muốn cơ sở dữ liệu của bạn tối ưu hóa các kế hoạch thực hiện tùy thuộc vào truy vấn thực tế. Hầu hết các cơ sở dữ liệu khi được cung cấp truy vấn đầy đủ là đủ thông minh để thực hiện một kế hoạch thực hiện tối ưu; ví dụ: nếu truy vấn trả về một tỷ lệ lớn của bảng, nó sẽ muốn đi qua toàn bộ bảng để tìm kết quả khớp; trong khi nếu nó chỉ nhận được một vài bản ghi, bạn có thể thực hiện tìm kiếm dựa trên chỉ mục [1] .

EDIT: Trả lời hai lời chỉ trích (đó là một chút quá dài cho các bình luận):

Đầu tiên, như những người khác lưu ý có, mọi cơ sở dữ liệu quan hệ hỗ trợ các câu lệnh được chuẩn bị và các tham số bị ràng buộc không nhất thiết phải biên dịch trước câu lệnh đã chuẩn bị mà không cần nhìn vào giá trị của các tham số bị ràng buộc. Nhiều cơ sở dữ liệu thường làm điều này, nhưng cơ sở dữ liệu cũng có thể xem xét các giá trị của các tham số bị ràng buộc khi tìm ra kế hoạch thực hiện. Đây không phải là vấn đề, vì cấu trúc của câu lệnh đã chuẩn bị với các tham số ràng buộc tách biệt, giúp cơ sở dữ liệu dễ dàng phân biệt rõ ràng câu lệnh SQL (bao gồm từ khóa SQL) với dữ liệu trong các tham số bị ràng buộc (trong đó không có gì trong tham số bị ràng buộc được hiểu là một từ khóa SQL). Điều này là không thể khi xây dựng các câu lệnh SQL từ nối chuỗi trong đó các biến và từ khóa SQL sẽ được trộn lẫn với nhau.

Thứ hai, như câu trả lời khác chỉ ra, sử dụng các tham số bị ràng buộc khi gọi một câu lệnh SQL tại một điểm trong chương trình sẽ ngăn chặn việc tiêm SQL một cách an toàn khi thực hiện cuộc gọi cấp cao nhất đó. Tuy nhiên, nếu bạn có lỗ hổng SQL SQL ở nơi khác trong ứng dụng (ví dụ: trong các hàm do người dùng xác định bạn đã lưu trữ và chạy trong cơ sở dữ liệu của mình, bạn đã viết một cách không an toàn để xây dựng các truy vấn SQL bằng cách nối chuỗi).

Ví dụ: nếu trong ứng dụng của bạn, bạn đã viết mã giả như:

sql_stmt = "SELECT create_new_user(?, ?)"
params = (email_str, hashed_pw_str)
db_conn.execute_with_params(sql_stmt, params)

Không thể có SQL tiêm khi chạy câu lệnh SQL này ở cấp ứng dụng. Tuy nhiên, nếu chức năng cơ sở dữ liệu do người dùng định nghĩa được viết không an toàn (sử dụng cú pháp PL/pgQuery):

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
   sql_str TEXT;
BEGIN
     sql_str := 'INSERT INTO users VALUES (' || email_str || ', ' || hashed_pw_str || ');'
     EXECUTE sql_str;
END;
$$
LANGUAGE plpgsql;

sau đó bạn sẽ dễ bị tấn công SQL, bởi vì nó thực thi một câu lệnh SQL được xây dựng thông qua nối chuỗi, trộn câu lệnh SQL với các chuỗi chứa các giá trị của các biến do người dùng xác định.

Điều đó nói rằng trừ khi bạn đang cố gắng không an toàn (xây dựng các câu lệnh SQL thông qua nối chuỗi), sẽ tự nhiên hơn khi viết người dùng định nghĩa theo cách an toàn như:

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
BEGIN
     INSERT INTO users VALUES (email_str, hashed_pw_str);
END;
$$
LANGUAGE plpgsql;

Hơn nữa, nếu bạn thực sự cảm thấy cần phải soạn một câu lệnh SQL từ một chuỗi trong hàm do người dùng xác định, bạn vẫn có thể tách các biến dữ liệu khỏi câu lệnh SQL giống như cách lưu trữ/tham số ràng buộc ngay cả trong hàm do người dùng xác định. Ví dụ: trong PL/pgSQL :

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
   sql_str TEXT;
BEGIN
     sql_str := 'INSERT INTO users VALUES($1, $2)'
     EXECUTE sql_str USING email_str, hashed_pw_str;
END;
$$
LANGUAGE plpgsql;

Vì vậy, sử dụng các câu lệnh đã chuẩn bị là an toàn khỏi việc tiêm SQL, miễn là bạn không làm những việc không an toàn ở nơi khác (đó là xây dựng các câu lệnh SQL bằng cách nối chuỗi).

54
dr jimbob

An toàn 100%? Thậm chí không gần gũi. Các tham số ràng buộc (được chuẩn bị theo tuyên bố hoặc cách khác) có hiệu quả có thể ngăn chặn, 100%, một lớp của lỗ hổng SQL SQL (giả sử không có lỗi db và triển khai lành mạnh ). Không có cách nào họ ngăn chặn các lớp khác. Lưu ý rằng PostgreSQL (db của sự lựa chọn của tôi) có một tùy chọn để liên kết các tham số với các câu lệnh ad hoc giúp tiết kiệm một chuyến đi khứ hồi về các câu lệnh được chuẩn bị nếu bạn không cần một số tính năng nhất định.

Bạn phải hình dung rằng nhiều cơ sở dữ liệu lớn, phức tạp là các chương trình. Độ phức tạp của các chương trình này thay đổi khá nhiều và SQL tiêm là thứ phải được xem xét trong các thói quen lập trình bên trong. Các thói quen như vậy bao gồm các trình kích hoạt, các hàm do người dùng xác định, các thủ tục được lưu trữ và các thứ tương tự. Không phải lúc nào cũng rõ ràng làm thế nào những thứ này tương tác từ cấp độ ứng dụng vì nhiều dba tốt cung cấp một mức độ trừu tượng giữa cấp độ truy cập ứng dụng và cấp độ lưu trữ.

Với các tham số bị ràng buộc, cây truy vấn được phân tích cú pháp, sau đó, trong PostgreQuery, ít nhất, dữ liệu được xem xét để lập kế hoạch. Kế hoạch được thực hiện. Với các báo cáo đã chuẩn bị, kế hoạch được lưu để bạn có thể thực hiện lại cùng một kế hoạch với dữ liệu khác nhau (điều này có thể hoặc không thể là điều bạn muốn). Nhưng vấn đề là với các tham số bị ràng buộc, một tham số không thể tiêm bất cứ thứ gì vào cây phân tích cú pháp. Vì vậy, lớp vấn đề tiêm SQL này được quan tâm đúng mức.

Nhưng bây giờ chúng ta cần phải ghi nhật ký ai viết gì vào bảng, vì vậy chúng ta thêm các kích hoạt và các hàm do người dùng định nghĩa để đóng gói logic của các kích hoạt này. Những vấn đề đặt ra mới. Nếu bạn có bất kỳ SQL động nào trong số này, thì bạn phải lo lắng về việc tiêm SQL ở đó. Các bảng họ viết có thể có các kích hoạt của riêng họ, v.v. Tương tự, một lệnh gọi hàm có thể gọi một truy vấn khác có thể gọi một lệnh gọi hàm khác và cứ thế. Mỗi trong số này được lập kế hoạch độc lập của cây chính.

Điều này có nghĩa là nếu tôi chạy một truy vấn có tham số ràng buộc như foo'; drop user postgres; -- thì nó không thể trực tiếp ngụ ý cây truy vấn cấp cao nhất và khiến nó thêm một lệnh khác để thả người dùng postgres. Tuy nhiên, nếu truy vấn này gọi hàm khác trực tiếp hay không, có thể ở đâu đó bên dưới dòng, một hàm sẽ dễ bị tổn thương và người dùng postgres sẽ bị loại bỏ. Các tham số ràng buộc được cung cấp không bảo vệ cho các truy vấn thứ cấp. Các truy vấn thứ cấp đó cần đảm bảo rằng chúng sử dụng các tham số ràng buộc quá mức có thể và nếu không, sẽ cần sử dụng các thói quen trích dẫn thích hợp.

Lỗ thỏ đi sâu.

BTW cho câu hỏi về Stack Overflow nơi vấn đề này là rõ ràng, xem https://stackoverflow.com/questions/37878426/conditable-where-expression-in-dynamic-query/37878574#37878574

Ngoài ra, một phiên bản có vấn đề hơn (do giới hạn về các câu lệnh tiện ích) tại https://stackoverflow.com/questions/38016764/perform-create-index-in-plpgsql-doesnt-run/38021245#38021245

25
Chris Travers