1. 12:05 07/09/2026
Khi xây dựng một API cho ứng dụng web hoặc mobile, nhiều lập trình viên thường chỉ tập trung vào việc làm sao cho endpoint chạy đúng logic nghiệp vụ mà quên mất ba lớp bảo vệ quan trọng: bảo mật dữ liệu, kiểm soát tần suất truy cập và đảm bảo tính nhất quán khi xử lý yêu cầu trùng lặp. Bài viết này sẽ chia sẻ cách tiếp cận thực tế để thiết kế API an toàn ngay từ giai đoạn đầu, kèm theo ví dụ minh họa cụ thể.
1. Bảo mật dữ liệu: Không chỉ là HTTPS
HTTPS là nền tảng bắt buộc, nhưng bảo mật dữ liệu còn nằm ở cách bạn thiết kế schema và xử lý dữ liệu nhạy cảm. Nguyên tắc đầu tiên là không bao giờ trả về toàn bộ đối tượng từ database trực tiếp qua API. Ví dụ, khi trả về thông tin người dùng, thay vì gửi toàn bộ record bao gồm password_hash, số điện thoại hay email nội bộ, hãy tạo một Data Transfer Object (DTO) chỉ chứa các trường cần thiết cho client. Điều này vừa giảm băng thông, vừa tránh rò rỉ dữ liệu nhạy cảm khi có lỗi.
Ngoài ra, hãy mã hóa dữ liệu nhạy cảm ở tầng database. Ví dụ, với ứng dụng y tế hoặc tài chính, số chứng minh nhân dân hoặc số thẻ tín dụng không nên lưu dạng plaintext. Sử dụng AES-256 cho dữ liệu cần giải mã khi đọc, và bcrypt hoặc argon2 cho mật khẩu. Một mẹo thực tế: nếu API của bạn có endpoint tìm kiếm người dùng theo email, hãy chuẩn hóa email (chuyển về chữ thường, trim khoảng trắng) trước khi lưu để tránh duplicate dữ liệu do khác biệt định dạng.
2. Rate Limiting: Bảo vệ backend khỏi quá tải và lạm dụng
Rate limiting không chỉ để chặn tấn công brute-force mà còn giúp ổn định hệ thống khi có đợt tăng đột biến người dùng. Cách phổ biến là thuật toán Token Bucket: mỗi client có một bucket chứa N token, mỗi request tiêu tốn một token, và token được nạp lại theo tốc độ R token/giây. Triển khai dễ dàng bằng Redis với key theo user_id hoặc IP, kèm TTL để tự động xóa key cũ.
Thực tế, bạn nên phân cấp giới hạn: ví dụ, endpoint đăng nhập cho phép 5 lần/phút mỗi IP, trong khi endpoint đọc danh sách sản phẩm cho phép 100 lần/phút. Khi vượt giới hạn, trả về HTTP 429 kèm header Retry-After để client biết thời gian chờ. Đừng quên ghi log các sự kiện bị chặn để phát hiện hành vi bất thường.
3. Idempotency: Tránh xử lý trùng lặp khi gọi lại
Idempotency rất quan trọng với các thao tác tạo hoặc thanh toán, nơi client có thể gửi lại request do mạng chập chờn. Cách triển khai phổ biến: client gửi header Idempotency-Key (một UUID duy nhất cho mỗi thao tác). Server kiểm tra key này trong Redis hoặc database; nếu đã tồn tại, trả về kết quả đã lưu thay vì xử lý lại. Kết hợp với transaction để đảm bảo tính nguyên tử: nếu lần đầu xử lý thất bại giữa chừng, key sẽ không được đánh dấu hoàn tất, cho phép thử lại.
Ví dụ thực tế: Khi người dùng bấm nút "Đặt hàng" hai lần do mạng chậm, nếu không có idempotency, hệ thống có thể tạo hai đơn hàng. Với Idempotency-Key, lần gọi thứ hai sẽ nhận đúng đơn hàng của lần đầu, tránh ghi nợ trùng.
4. Lời khuyên áp dụng
Bắt đầu từ việc viết một checklist bảo mật cho từng endpoint: có cần xác thực không, có validate input đầu vào không, có giới hạn tần suất không, có cần idempotency không. Xây dựng middleware dùng chung cho các lớp này thay vì viết riêng cho từng route. Cuối cùng, hãy viết test tự động cho các tình huống: gửi request trùng key, vượt rate limit, và gửi dữ liệu thiếu trường. Chi phí đầu tư ban đầu nhỏ nhưng giúp bạn tránh được những sự cố tốn kém về sau khi sản phẩm đã có người dùng thật.