Cloudflare Turnstile — "Lá Chắn" Chống Người Máy (Bot Protection) 🛡️¶
Turnstile Là Cái Gì Vậy?¶
Chắc hẳn bạn rất ghét việc đang lướt web thì bị bắt "Chọn tất cả hình có cột đèn giao thông", đúng không? Turnstile chính là giải pháp thay thế hệ thống Google reCAPTCHA cũ rích đó do Cloudflare tạo ra. Nó thông minh hơn, thân thiện hơn và đặc biệt là không làm phiền người dùng.
Thử so sánh nhẹ nhé:
| Tiêu chí | reCAPTCHA v2 (Cũ) | reCAPTCHA v3 | Turnstile (Mới & Xịn) |
|---|---|---|---|
| Trải nghiệm (UX) | Phải giải đố mỏi mắt | Chạy ngầm | Chạy ngầm (Thường tự động cho qua) |
| Quyền riêng tư | Bị Google theo dõi | Bị Google theo dõi | Tôn trọng quyền riêng tư tuyệt đối |
| Độ chính xác | Tốt | Tốt | Rất Tốt |
| Miễn phí không? | ✅ Có | ✅ Có | ✅ Có |
| Cần tài khoản Google? | ✅ Bắt buộc | ✅ Bắt buộc | ❌ Không cần luôn |
Turnstile Hoạt Động Như Thế Nào? ⚙️¶
Tưởng tượng Turnstile như một anh bảo vệ thông minh đứng trước cửa nhà bạn:
- Nhìn mặt bắt hình dong: Trình duyệt tải khung Turnstile lên. Lúc này nó sẽ ngầm phân tích xem người đang gõ phím có giống con người không (qua cách di chuột, địa chỉ IP...).
- Nếu chắc chắn là người thật: Cửa tự mở (Tick xanh luôn).
- Nếu hơi nghi ngờ: Mới bắt bấm vào cái ô "Verify you are human".
- Phát thẻ: Sau khi qua cửa, Turnstile cấp cho người dùng một cái "thẻ chứng nhận" (là một đoạn mã dài ngoằng).
- Trình diện thẻ: Trình duyệt gửi cái thẻ này kèm với thông tin đăng nhập lên cho Server (Bộ não).
- Gọi điện kiểm chứng: Server không tự tin lắm nên gọi điện thẳng lên tổng đài Cloudflare: "Alo, cái thẻ này số hiệu xxx, có đúng là hàng thật chưa qua sử dụng không?".
- Quyết định:
- Tổng đài báo "Hàng chuẩn" → Cho phép đăng nhập bình thường.
- Tổng đài báo "Hàng fake" → Đuổi ra ngoài, báo lỗi!
Lắp Ráp Mặt Tiền (Giao diện Frontend) 🖥️¶
Đầu tiên, bạn cần viết một cái khung nhỏ (Widget) để hiển thị Turnstile:
// web/src/components/TurnstileWidget.tsx
import { useEffect, useRef } from 'react';
interface Props {
onVerify: (token: string) => void;
onExpire?: () => void;
}
export default function TurnstileWidget({ onVerify, onExpire }: Props) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
// Nếu chưa load xong hoặc thiếu script thì bỏ qua
if (!ref.current || !window.turnstile) return;
// Hiển thị khung Turnstile lên màn hình
const widgetId = window.turnstile.render(ref.current, {
sitekey: import.meta.env.VITE_TURNSTILE_SITE_KEY, // Mã định danh web của bạn
callback: onVerify, // Gọi hàm này khi xác nhận thành công
'expired-callback': onExpire, // Gọi hàm này khi thẻ bị hết hạn (do để lâu quá)
});
// Dọn dẹp khi chuyển trang
return () => window.turnstile.remove(widgetId);
}, []);
return <div ref={ref} />;
}
Đừng quên bước này: Nhét dòng này vào file index.html của bạn để tải công cụ của Cloudflare về nhé:
<script src="[https://challenges.cloudflare.com/turnstile/v0/api.js](https://challenges.cloudflare.com/turnstile/v0/api.js)" async defer></script>
Cách Gắn Nó Vào Form Đăng Nhập¶
function LoginPage() {
const [turnstileToken, setTurnstileToken] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
// Bắt buộc phải có thẻ (Tick xanh) rồi mới cho bấm nút
if (!turnstileToken) return;
// Gửi kèm cái thẻ đó lên server
await api.auth.login({ email, password, turnstileToken });
};
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" />
<input name="password" type="password" />
{/* Cài anh bảo vệ vào đây */}
<TurnstileWidget onVerify={setTurnstileToken} />
<button type="submit">Đăng nhập</button>
</form>
);
}
Xử Lý Ở Lõi (Backend Server) 🧠¶
Máy chủ của bạn (Worker) phải cầm cái thẻ đó đi hỏi lại Cloudflare. Không bao giờ được tin tưởng phía Frontend nhé!
// api/src/utils.ts
export async function verifyTurnstile(token: string, secretKey: string): Promise<boolean> {
try {
// Gọi điện lên tổng đài Cloudflare để xác minh
const res = await fetch('[https://challenges.cloudflare.com/turnstile/v0/siteverify](https://challenges.cloudflare.com/turnstile/v0/siteverify)', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ secret: secretKey, response: token }),
});
const data = await res.json() as { success: boolean; 'error-codes'?: string[] };
return data.success; // Trả về true nếu thẻ xịn, false nếu thẻ fake
} catch {
return false; // Lỡ rớt mạng thì cứ từ chối cho an toàn
}
}
// Gắn vào chỗ xử lý Đăng nhập
auth.post('/login', async (c) => {
const { email, password, turnstileToken } = await c.req.json();
// Kiểm tra thẻ trước khi làm bất cứ việc gì khác
const isHuman = await verifyTurnstile(turnstileToken, c.env.TURNSTILE_SECRET_KEY);
if (!isHuman) {
return c.json({ error: 'Ê, bạn là robot đúng không?' }, 400);
}
// Thẻ xịn rồi thì mới kiểm tra email/password...
});
Các Bước Cài Đặt Ban Đầu 🛠️¶
1. Xin Giấy Phép Trên Cloudflare¶
- Vào Trang quản lý Cloudflare, tìm mục Turnstile bên cột trái.
- Bấm "Add site" (Thêm trang web) → Nó sẽ cấp cho bạn 2 cái chìa khóa: Site Key (chìa công khai, ai cũng thấy được) và Secret Key (Chìa bí mật, cấm để lộ).
2. Cất Chìa Khóa Vào Code¶
# Bên Giao diện (Lưu vào file .env.local) - Chìa công khai
VITE_TURNSTILE_SITE_KEY=0x4AAA...
# Bên Não bộ (Gõ lệnh trên Terminal để Cloudflare cất giùm) - Chìa bí mật
wrangler secret put TURNSTILE_SECRET_KEY
3. Đồ Nghề Chạy Thử (Test Mode)¶
Lúc đang lập trình ở nhà, bạn không cần phải dùng chìa khóa thật, Cloudflare cho sẵn bộ chìa khóa "giả" để test cho lẹ:
- Chìa công khai:
1x00000000000000000000AA→ Cứ gắn vào là nó báo tick xanh (Thành công). - Chìa công khai:
2x00000000000000000000AB→ Gắn vào là nó chửi (Thất bại). - Chìa bí mật tương ứng để test:
1x0000000000000000000000000000000AA.
Nên Đặt "Anh Bảo Vệ" Này Ở Những Cửa Nào? 🚪¶
Trong dự án này, chúng ta chỉ đặt Turnstile ở những chỗ nhạy cảm, dễ bị kẻ xấu spam:
| Nơi đặt | Lý do cần bảo vệ |
|---|---|
POST /auth/login (Đăng nhập) |
Chống hacker dùng máy móc dò mật khẩu liên tục |
POST /auth/join (Tham gia nhóm) |
Tránh việc bị tạo hàng ngàn tài khoản rác |
POST /auth/setup (Tạo sếp lớn) |
Chống kẻ gian cướp quyền khởi tạo hệ thống |
Lưu ý: KHÔNG cần nhét nó vào những đường link đã được bảo vệ bằng thẻ JWT (đã đăng nhập). Khách VIP đã có thẻ rồi thì bắt họ chứng minh là người thật làm gì nữa, phiền lắm!