Courses
SQL injection (viết tắt là SQLi) là một trong những mánh khóe lâu đời trong sổ tay hacker, nhưng vẫn cực kỳ phổ biến và nguy hiểm. Nói ngắn gọn, đó là cách đánh lừa cơ sở dữ liệu tiết lộ những điều mà lẽ ra nó không được phép.
Trong bài viết này, tôi sẽ hướng dẫn bạn SQL injection thực sự là gì, những cách khác nhau mà kẻ tấn công sử dụng nó, một số ví dụ thực tế gây thiệt hại nghiêm trọng, và quan trọng hơn cả là cách bạn có thể phòng tránh. Dù bạn là nhà phát triển hay chỉ tò mò về cách mọi thứ bị phá vỡ trên internet, bạn sẽ có được hiểu biết vững chắc về SQLi (và sẽ không buồn ngủ giữa chừng, tôi hứa).
SQL Injection là gì?
SQL injection là một dạng tấn công xảy ra khi ai đó tìm được cách can thiệp vào các truy vấn SQL mà ứng dụng của bạn gửi tới cơ sở dữ liệu. Bình thường, các truy vấn đó dùng để thực hiện những việc như lấy hồ sơ người dùng hoặc cập nhật danh sách sản phẩm. Nhưng với SQLi, kẻ tấn công có thể chèn các đoạn mã SQL độc hại vào các trường nhập liệu (như thanh tìm kiếm hoặc biểu mẫu đăng nhập), và đột nhiên cơ sở dữ liệu làm đúng theo điều họ muốn.
Vì sao chuyện này hiệu quả?
Bởi vì ở đâu đó trong quy trình, ứng dụng tin tưởng dữ liệu người dùng quá mức và coi nó như văn bản vô hại thay vì coi là mã có thể thực thi. Giống như bạn để ai đó điền vào biểu mẫu rồi dán nguyên văn những gì họ viết thẳng vào bảng điều khiển lệnh vậy.
Tại sao nó nguy hiểm?
SQL injection nguy hiểm vì nó có thể được dùng để xem hoặc đánh cắp dữ liệu riêng tư (như tên người dùng, mật khẩu, hoặc thông tin thẻ tín dụng), vượt qua màn hình đăng nhập, xóa hoặc sửa dữ liệu, và thậm chí trong kịch bản tệ nhất là giành quyền kiểm soát hoàn toàn máy chủ cơ sở dữ liệu.
Vậy nên, đúng là SQLi rất tệ, và đây chẳng phải mối lo bảo mật mới mẻ gì, nhưng bạn sẽ ngạc nhiên khi thấy có bao nhiêu ứng dụng không được bảo vệ đúng cách trước nó.
Các loại SQL Injection
Tùy vào cách kẻ tấn công tương tác với ứng dụng của bạn và loại phản hồi chúng nhận được, SQLi có vài "hương vị" khác nhau. Có 3 loại chính mà bạn có thể gặp:
In-band SQLi
Đây là loại trực diện nhất. Kẻ tấn công gửi một truy vấn SQL độc hại và nhận kết quả trả về ngay qua ứng dụng. Nhanh và thường rất hiệu quả.
-
Error-based SQLi: Kỹ thuật này dựa vào việc cơ sở dữ liệu "nhiệt tình" trả về thông báo lỗi. Những lỗi này có thể để lộ nhiều thông tin hữu ích, như tên bảng hoặc cấu trúc truy vấn, giúp kẻ tấn công dễ dàng lên bước tiếp theo.
-
Union-based SQLi: Ở đây, kẻ tấn công dùng toán tử
UNIONđể kết hợp truy vấn của chúng với truy vấn mà ứng dụng đang chạy. Đây là cách khéo léo để rút thêm dữ liệu từ cơ sở dữ liệu và "nhét" nó vào phản hồi.
Inferential SQLi (còn gọi là Blind SQLi)
Loại này kín đáo hơn. Thay vì thấy trực tiếp kết quả truy vấn, kẻ tấn công quan sát cách ứng dụng hành xử để suy ra chuyện gì đang diễn ra bên trong.
- Boolean-based (content-based) SQLi: Kẻ tấn công tinh chỉnh truy vấn với các điều kiện đúng hoặc sai (như 1=1 hoặc 1=2) và quan sát cách trang thay đổi. Nó tải bình thường? Hiện lỗi? Hay hành xử lạ?
- Time-based SQLi: Kỹ thuật này thêm độ trễ thời gian vào truy vấn (ví dụ
WAITFOR DELAY '00:00:05') và dùng thời gian phản hồi để suy ra một điều kiện là đúng hay sai.
Out-of-band SQLi (khi mọi thứ trở nên tinh vi)
Loại này ít gặp hơn, nhưng vẫn đáng biết. Out-of-band SQLi không dựa vào phản hồi tức thì từ ứng dụng. Thay vào đó, kẻ tấn công dùng các kênh khác như yêu cầu DNS hoặc HTTP để trích xuất dữ liệu. Thường dùng trong tình huống không thể nhận phản hồi trực tiếp nhưng máy chủ cơ sở dữ liệu có truy cập internet (và trong 90% trường hợp, điều đó có lẽ là không nên).
Các kỹ thuật SQL Injection thường gặp
Rồi, chúng ta đã biết 3 loại SQL injection. Nhưng trong thực tế, kẻ tấn công thực hiện điều đó như thế nào?
Tấn công OR 1=1
Đây là kinh điển. Hãy hình dung biểu mẫu đăng nhập nơi bạn phải nhập tên người dùng và mật khẩu. Kẻ tấn công có thể nhập thứ gì đó như sau:
' OR 1=1 --
Truy vấn SQL sẽ trông như thế này:
SELECT * FROM users WHERE username = '' OR 1=1 --' AND password = '';
Vì 1=1 luôn đúng, cơ sở dữ liệu trả về tất cả người dùng, và -- biến phần còn lại của truy vấn thành chú thích. Nếu không có giới hạn, kẻ tấn công có thể đăng nhập mà không cần biết tên người dùng hợp lệ.

Nguồn: vmware
Comment injection
Như vừa thấy ở trên, các ký tự -- hoặc /* */ được dùng để chú thích (comment) một phần câu lệnh SQL. Kẻ tấn công dùng điều này để loại bỏ các mệnh đề thừa có thể làm hỏng payload chèn vào. Ví dụ, nếu chúng không biết đầy đủ cấu trúc truy vấn gốc, chúng có thể cắt bỏ phần sau để truy vấn hợp lệ về mặt cú pháp.
Batch SQL statements
Một số cơ sở dữ liệu cho phép chạy nhiều câu lệnh SQL trong một yêu cầu, ngăn cách bằng dấu chấm phẩy. Tính năng này có thể bị hacker lợi dụng để gây đủ thứ thiệt hại, như sửa dữ liệu hoặc thậm chí xóa bảng nếu ứng dụng không hạn chế.
'; DROP TABLE users; --
Tấn công dựa trên UNION
Bằng cách chèn một câu lệnh UNION SELECT , kẻ tấn công có thể kết hợp truy vấn độc hại với truy vấn hợp lệ và lấy kết quả từ các bảng khác như dữ liệu người dùng nhạy cảm, mật khẩu, hoặc thông tin thẻ tín dụng. Về bản chất là tận dụng một truy vấn sẵn có để lấy thêm dữ liệu "bonus".
Blind SQL injection
Chúng ta đã nhắc tới trước đó. Ngay cả khi ứng dụng không hiển thị lỗi hoặc trả về kết quả truy vấn, kẻ tấn công vẫn có thể rút dữ liệu từng chút một bằng cách quan sát hành vi. Cách này chậm và tỉ mỉ hơn, nhưng hiệu quả. Thường được tự động hóa bằng các công cụ có thể thử hàng trăm truy vấn và phản hồi dựa trên thời gian.
Stored SQL injection
Mã SQL độc hại được lưu trong cơ sở dữ liệu, như trong hồ sơ người dùng hoặc bình luận, và được thực thi sau này khi ai đó xem dữ liệu đó.
Các cuộc tấn công SQL Injection thực tế
SQL injection có thể nghe như chuyện của giới hacker, nhưng qua nhiều năm nó đã gây thiệt hại rất thật. Mà thiệt hại ở đây là hàng triệu bản ghi người dùng bị lộ, tiền phạt khổng lồ, và những dòng tít báo chẳng mấy hay ho cho bộ phận marketing.
Guess.com (2002)
Đây là một ví dụ sớm khiến mọi người bắt đầu chú ý. Một nhà nghiên cứu bảo mật đã khai thác lỗ hổng SQL injection và truy cập hơn 200.000 hồ sơ khách hàng, bao gồm cả chi tiết thẻ tín dụng. Tin tốt là nó được phát hiện và báo cáo bởi một hacker mũ trắng.
Heartland Payment Systems (2009)
Đây là vụ rò rỉ quy mô lớn ảnh hưởng đến một nhà xử lý thanh toán, hơn 130 triệu số thẻ tín dụng bị đánh cắp. Kẻ tấn công dùng SQL injection để đặt chân vào hệ thống, rồi leo thang từ đó. Vụ này thường được nhắc tới như một trong những vụ rò rỉ dữ liệu lớn nhất từng có.
Yahoo! Voices (2012)
Kẻ tấn công khai thác lỗ hổng SQL injection dựa trên UNION và làm lộ 450.000 tên người dùng cùng mật khẩu dạng văn bản thuần. Vâng, bạn đọc đúng rồi đấy, thông tin đăng nhập thô được lưu ở dạng plain-text. Vụ việc đặc biệt đáng xấu hổ vì Yahoo! là một công ty công nghệ lớn, và lưu mật khẩu không mã hóa là điều "tối kỵ", ngay cả với tiêu chuẩn năm 2012.
TalkTalk (2015)
TalkTalk, một nhà cung cấp viễn thông nổi tiếng tại Anh, đã trở thành nạn nhân của một cuộc tấn công SQL injection khá cơ bản. Khoảng 157.000 hồ sơ khách hàng bị xâm phạm, và công ty bị phạt £400.000. Một trong những kẻ tấn công chỉ mới 17 tuổi.
Gab (2021)
Những nhà hoạt động hacktivist đã dùng SQL injection để trích xuất 70GB dữ liệu, bao gồm bài đăng riêng tư và mật khẩu đã băm. Vụ rò rỉ mang màu sắc chính trị, và hệ quả càng làm dấy lên sự soi xét về tư thế bảo mật tổng thể của Gab.
Cách ngăn chặn SQL Injection
Sau khi tự làm mình sợ đôi chút với những vụ rò rỉ ngoài đời, hãy nói về giải pháp. Tin vui là ngăn chặn SQL injection không phải khoa học tên lửa. Phần lớn là không tin tưởng dữ liệu người dùng và bám sát các thực hành tốt. Bạn có thể làm gì:
Dùng truy vấn tham số hóa
Đây là quy tắc số một. Đừng bao giờ tạo truy vấn SQL bằng cách nối chuỗi. Thay vào đó, dùng placeholder và truyền đầu vào của người dùng như tham số. Hầu hết framework và thư viện đều hỗ trợ rất dễ.
Ví dụ trong Node.js với pg (PostgreSQL):
client.query('SELECT * FROM users WHERE id = $1', [userId]);
Về bản chất, điều này nói với cơ sở dữ liệu rằng “đây là cấu trúc truy vấn, và đây là dữ liệu, làm ơn đừng trộn lẫn chúng.”
Dùng stored procedure một cách thông minh
Stored procedure có thể giúp ích, nhưng chỉ khi chúng cũng tránh SQL động. Ý tưởng là logic SQL nằm trong cơ sở dữ liệu, và ứng dụng của bạn chỉ gọi những khối logic an toàn, được định nghĩa sẵn đó.
Nếu bạn muốn học cách tạo, cập nhật và thực thi function và stored procedure, hãy xem khóa học Writing Functions and Stored Procedures in SQL Server của chúng tôi.
Xác thực và làm sạch dữ liệu đầu vào
Nếu ứng dụng của bạn cần một con số, hãy đảm bảo đó là con số. Đừng mù quáng chấp nhận mọi thứ người dùng đưa vào. Dùng kiểm tra kiểu dữ liệu, giới hạn độ dài, và danh sách cho phép (ví dụ chỉ cho phép các giá trị đã biết là hợp lệ) khi có thể.
Escape ký tự (như dấu nháy) cũng có thể hữu ích, nhưng không thể thay thế cho truy vấn tham số hóa.
Dùng Web Application Firewall (WAF)
WAF có thể là tuyến phòng thủ đầu tiên, đặc biệt trước các mẫu tấn công đã biết. Nó có thể chặn một số lưu lượng độc hại trước khi chạm tới ứng dụng của bạn. Không phải vạn năng nhưng hữu ích, hơi giống một bộ lọc thư rác cho SQL của bạn.
Áp dụng nguyên tắc đặc quyền tối thiểu
Ứng dụng web của bạn không nên kết nối với cơ sở dữ liệu bằng quyền quản trị. Hạn chế những gì mỗi người dùng hoặc dịch vụ có thể làm. Ví dụ, nếu ứng dụng của bạn chỉ cần đọc dữ liệu, đừng cấp quyền xóa bảng. Quyền hạn càng ít, thiệt hại do injection gây ra càng giảm.
Kiểm thử & phát hiện SQL Injection
Ngay cả khi bạn nghĩ mã của mình an toàn, vẫn đáng để kiểm thử như cách một kẻ tấn công sẽ làm. SQL injection có cách len lỏi qua kẽ hở, nhất là ở những codebase lớn hoặc hệ thống cũ. Có các công cụ và kỹ thuật giúp phát hiện dễ hơn và cũng khá thú vị để thử.
Kiểm thử thủ công
Đôi khi cách nhanh nhất để phát hiện lỗ hổng là thử chọc vào nó. Hãy thử nhập ' OR 1=1 -- or '; DROP TABLE users; -- vào các trường biểu mẫu, URL, hoặc bất kỳ khu vực nhập liệu nào và xem ứng dụng phản ứng ra sao. Nếu bạn thấy lỗi lạ, dữ liệu bất ngờ, hoặc thông báo thành công khó hiểu, có thể bạn đã tìm thấy điều gì đó đáng ngờ.
Mẹo nhỏ: Luôn kiểm thử ở môi trường dev hoặc staging.
Trình quét tự động
Có rất nhiều công cụ sẽ "chọc" thay cho bạn. Một vài cái tên hay:
- sqlmap: Công cụ mã nguồn mở mạnh mẽ tự động hóa phát hiện và khai thác (trong các bối cảnh kiểm thử hợp pháp).
- Burp Suite: Tuyệt vời cho bảo mật ứng dụng web nói chung, có các extension để phát hiện SQLi.
- OWASP ZAP: Miễn phí và thân thiện với người mới, được xây dựng để tìm nhiều lỗ hổng web.
Các công cụ này mô phỏng tấn công, gắn cờ các điểm có thể chèn và đôi khi còn gợi ý cách khắc phục.
Phân tích log
Một mẹo khác: theo dõi log cơ sở dữ liệu. Các truy vấn thất bại lặp lại, mẫu cú pháp lạ, hoặc lượng yêu cầu tăng đột biến có chứa ', --, hoặc UNION SELECT đều là cờ đỏ.
Kết luận
SQL injection sẽ chưa biến mất sớm đâu, và là một trong những mối đe dọa không bao giờ "lỗi thời". Nó đơn giản, mạnh mẽ, và nếu bạn không cẩn thận, có thể gây ra thiệt hại nghiêm trọng. Vấn đề là SQLi hoàn toàn có thể phòng tránh. Bằng cách dùng truy vấn tham số hóa, xác thực đầu vào, áp dụng nguyên tắc đặc quyền tối thiểu và kiểm thử thường xuyên, bạn có thể bảo vệ ứng dụng và người dùng khỏi tác động tàn phá của tấn công SQL injection.
Hãy nhớ, không ai đòi hỏi sự hoàn hảo, nhưng với chút chủ động, bạn có thể giữ cơ sở dữ liệu an toàn và gạch bỏ mục này khỏi danh sách kiểm tra bảo mật. Và nếu bạn nghiêm túc với SQL và muốn chứng minh kỹ năng trước nhà tuyển dụng tiềm năng, hãy xem Chứng chỉ SQL Associate của chúng tôi! Nó sẽ cho thấy bạn có khả năng dùng SQL để trích xuất dữ liệu phù hợp từ cơ sở dữ liệu và dùng nó để trả lời các câu hỏi dữ liệu phổ biến.

Tôi là một trưởng nhóm kỹ thuật định hướng sản phẩm, chuyên giúp các startup giai đoạn đầu phát triển từ nguyên mẫu đầu tiên đến khi đạt được sự phù hợp sản phẩm - thị trường và xa hơn nữa. Tôi luôn tò mò về cách con người sử dụng công nghệ, và tôi thích làm việc sát sao với nhà sáng lập và các nhóm liên chức năng để biến những ý tưởng táo bạo thành hiện thực. Khi không xây dựng sản phẩm, tôi tìm cảm hứng ở những miền đất mới hoặc xả căng thẳng tại phòng tập yoga.
FAQs
SQL injection có thể được dùng để tấn công cơ sở dữ liệu NoSQL không?
SQL injection truyền thống không hoạt động trên cơ sở dữ liệu NoSQL như MongoDB, nhưng các cuộc tấn công kiểu injection tương tự vẫn có thể xảy ra nếu dữ liệu đầu vào không được làm sạch đúng cách (chẳng hạn như chèn tài liệu hoặc thao túng truy vấn)
Các framework hiện đại giúp ngăn chặn SQL injection như thế nào?
Nhiều framework hiện đại (như Django, Laravel hoặc Spring) tích hợp sẵn các lớp bảo vệ như ORM và tham số hóa tự động, giúp khó vô tình viết truy vấn dễ bị tấn công hơn. Tuy nhiên, bạn vẫn phải dùng chúng đúng cách!
Ứng dụng di động cũng có nguy cơ bị SQL injection không?
Chắc chắn rồi. Nếu một ứng dụng di động giao tiếp với backend API xây dựng các truy vấn SQL không an toàn dựa trên dữ liệu người dùng, nó vẫn dễ bị tấn công như thường, ngay cả khi frontend có vẻ được khóa chặt.
Kẻ tấn công tìm các website dễ bị SQL injection nhắm mục tiêu bằng cách nào?
Họ thường dùng trình quét tự động hoặc “Google dorking” (các truy vấn tìm kiếm đặc biệt) để tìm các trang có trường nhập liệu hoặc tham số URL có thể khai thác.