- REST API trong WordPress: Lợi bất cập hại nếu không kiểm soát
- Ba kiểu bảo mật nửa mùa mà mình từng gặp (và tránh xa)
- 1. Dùng nonce để “bảo vệ” nhưng page đang bị cache toàn phần
- 2. Lưu địa chỉ IP vào transient để hạn chế spam
- 3. Route ghi dữ liệu mà không hề kiểm tra
- Cách mình tiếp cận REST API: đơn giản, rõ ràng, có chủ đích
- 1. Endpoint public, chỉ đọc
- 2. Endpoint ghi dữ liệu
- Trường hợp thực tế: Init View Count
- “Spam view” có phải là mối lo?
- Kết luận
REST API trong WordPress: Lợi bất cập hại nếu không kiểm soát
WordPress từ phiên bản 4.7 đã tích hợp sẵn REST API. Các plugin và theme có thể dễ dàng đăng ký route mới để trả về dữ liệu JSON, hoặc thậm chí ghi dữ liệu xuống hệ thống. Điều này rất phù hợp với các ứng dụng hiện đại (SPA, headless), nhưng đi kèm một sự thật nghiêm trọng: WordPress không tự bảo vệ giúp bạn.
REST route nào bạn tạo ra sẽ hoạt động y như bạn viết – kể cả khi ai đó gọi từ bên ngoài, spam POST liên tục, hoặc lạm dụng để tải về toàn bộ nội dung ẩn. Nếu không có bước bảo vệ hợp lý, hệ thống sẽ bị tổn thương dần đều mà bạn không hề hay biết.
Ba kiểu bảo mật nửa mùa mà mình từng gặp (và tránh xa)
1. Dùng nonce để “bảo vệ” nhưng page đang bị cache toàn phần
Đây là lỗi kinh điển. Dev tạo một nonce qua wp_create_nonce('wp_rest'), nhúng nó vào JS bằng wp_localize_script(), rồi yên tâm gọi REST API từ frontend. Nhưng nếu trang đang bị cache toàn phần (như khi dùng WP Rocket hoặc Cloudflare “cache everything”), thì đoạn HTML – bao gồm cả nonce – sẽ bị đóng băng. Khi nonce hết hạn (sau 12 tiếng), mọi request từ frontend đều thất bại vì không xác thực được. Kết quả: tính năng không hoạt động, không có cảnh báo rõ ràng, người dùng không biết chuyện gì xảy ra.
Mình đã từng debug một plugin bị lỗi sau 3 ngày chạy ổn – chỉ vì nonce được cache cứng trong file HTML. Đó là bài học không bao giờ quên.
2. Lưu địa chỉ IP vào transient để hạn chế spam
Nhiều người nghĩ đơn giản: ghi IP vào transient, set thời gian hết hạn là 60 giây, ai gọi trùng IP trong 60s thì bỏ qua. Nghe hợp lý, nhưng không tưởng tượng được hậu quả: nếu site có 1 triệu view/ngày, tức là bạn sẽ có 1 triệu transient – tương đương 1 triệu dòng trong bảng wp_options. Cái bảng vốn nên chỉ lưu cấu hình nhẹ nhàng giờ thành nơi chứa rác.
Chưa kể: delete_expired_transients() chỉ chạy khi có request vào, và nhiều host shared không cho xóa bulk. WP DB Cleaner cũng không cứu nổi nếu bị spam quá nhiều.
3. Route ghi dữ liệu mà không hề kiểm tra
Đây là lỗi nguy hiểm nhất. Một route được đăng ký kiểu:
register_rest_route('plugin/v1', '/count', [
'methods' => 'POST',
'callback' => 'plugin_count',
'permission_callback' => '__return_true',
]);
Sau đó ghi trực tiếp dữ liệu vào database, không kiểm tra xem post_id có tồn tại không, bài viết có phải dạng publish không, loại post có hợp lệ không… Dữ liệu ghi xuống là thật, nhưng hoàn toàn không có kiểm soát.
Cách mình tiếp cận REST API: đơn giản, rõ ràng, có chủ đích
Mình luôn chia REST API ra làm hai nhóm:
1. Endpoint public, chỉ đọc
- Không yêu cầu nonce
- Không kiểm tra quyền
- Chỉ trả dữ liệu công khai: title, ID, permalink, thumbnail
- Nếu có filter, luôn cho phép dev giới hạn lại truy vấn
2. Endpoint ghi dữ liệu
- Không bao giờ tin vào dữ liệu client gửi lên
- Không lưu IP, user ID, hay dữ liệu nhạy cảm
- Chỉ chấp nhận post dạng
publishvà post_type nằm trong danh sách whitelist - Nếu có ghi xuống, ghi ở mức nhẹ nhất – ví dụ: chỉ tăng số lượt xem
Trường hợp thực tế: Init View Count
Trong plugin Init View Count, mình có một route REST API là /initvico/v1/count. Endpoint này được gọi từ frontend sau khi người dùng cuộn trang hoặc đợi đủ delay. Dữ liệu ghi xuống chỉ là số lượt xem của bài viết đó.
Mình không dùng nonce – vì lý do đơn giản: trang có thể được cache toàn phần. Việc nhét nonce vào HTML là vô nghĩa khi Cloudflare hoặc plugin cache giữ nguyên mã nguồn hàng giờ. Thay vì thế, mình bảo vệ bằng cách:
- Chỉ ghi lượt xem nếu bài viết tồn tại, là dạng publish, và đúng loại post
- Không lưu IP hay token nào từ user
- Cho phép filter
init_plugin_suite_view_count_should_countđể chặn điều kiện nếu cần
“Spam view” có phải là mối lo?
Nhiều người sợ rằng ai đó sẽ viết script spam lượt xem. Câu trả lời là: nếu spam view mà không làm sập site, không tạo load, không phá dữ liệu – thì có đáng lo không? Với mình thì không. Ai đó viết script để giúp bài viết được tính thêm lượt xem là đang tăng traffic cho blog – mình còn nên cảm ơn.
Nếu cần bảo vệ thật sự? Hãy dùng Cloudflare. Họ có hệ thống phát hiện bot, giới hạn request, lọc IP và kiểm tra hành vi trình duyệt. Không cần bạn viết thêm dòng code nào cả.
Kết luận
Bảo mật không phải là chuyện thêm mã xác thực càng nhiều càng tốt. Bảo mật đúng là hiểu rõ hệ thống mình đang dùng, biết endpoint nào cần bảo vệ, endpoint nào không cần, và luôn đánh đổi giữa an toàn và hiệu suất. Một lớp bảo vệ sai chỗ có thể không chặn được hacker nào nhưng lại làm chậm site, lỗi chức năng, hoặc khiến bạn đau đầu debug suốt tuần.
Đừng làm bảo mật nửa mùa. Hoặc làm cho đáng, hoặc bỏ luôn. Làm đúng, làm rõ, làm có chủ đích – đó mới là mindset bền vững.
Bình luận