طراحی یک Human Verification داخلی بدون سرویس خارجی
در بسیاری از پروژهها، برای تشخیص انسان از ربات از reCAPTCHA استفاده میکنیم اگر از ورژن ۳ گوگل استفاده کرده باشین متوجه میشین که ریکپچا به صورت بصری به کاربر دیگه نمایش داده نمیشه و در پشت صحنه بروزر میاد با استفاده از توکنی که میسازه و به بکند ارسال میکنه انسان بودنشو به کد ما اثبات میکنه . فعلا با توجه به وضعیت ایران و ملی شدن اینترنت دیگه دسترسی به سرویس های خارجی نداریم یا اگرم داریم یه روز وصله یه روز قطع
در این مقاله میخوام دربارهی تجربهی طراحی یک Human Verification صحبت کنم؛
راهحلی که نه به Google وابسته است، نه Cloudflare، نه هیچ سرویس خارجی دیگر — و در عین حال UX کاربر مشکلی ایجاد نمیکند. ولی بدونید راه حل موقت است و همچنین برای فرم های حساس بهتره اسفاده نکنین . همچنین هدف این کد سمت فرانت این هستش که از سابمیت شدن فرم ها توسط ربات ها جلوگیری شه برای امنیت بیشتر به سراغ ریت لیمیت برید .
بهجای اینکه از کاربر بخواهیم چیزی را حل کند ،میتوانیم بررسی کنیم چطور رفتار میکند.
انسانها:
زمان میبرند
اسکرول میکنند
لمس میکنند
فوکوس میگیرند
تعامل دارند
باتها (حداقل بخش بزرگی از آنها):
سریعاند
بدون تعامل
بدون فوکوس
و اغلب headless
ایدهی اصلی همینجا شکل گرفت:
جمعآوری سیگنالهای رفتاری در فرانتاند و تصمیمگیری در بکاند.
فرانتاند یک توکن میسازد که در واقع خلاصهای از رفتار کاربر است.این توکن هیچ تصمیمی نمیگیرد؛ فقط اطلاعات خام را منتقل میکند.
محتوای واقعی توکن (قبل از encode شدن):
{ "action": "submit-contact", "d": 2400, "i": 6, "k": 2, "f": 1 }
d → مدت حضور کاربر در صفحه
i → تعداد تعامل (touch / scroll / click)
k → تعداد تایپ (اختیاری)
f → فوکوس صفحه
action → جلوگیری از reuse توکن
این آبجکت فقط Base64 میشود و همراه فرم به بکاند ارسال میشود.
در طراحی اولیه ممکن است باید بدونیم موبایل ماوس ندارد و از تاچ استفاده میشه.به همین دلیل، بهجای «حرکت ماوس» مفهوم کلیتری تعریف شد:
Interaction Count
که شامل:
touch
scroll
click
focus
میشود.
به این شکل، منطق تشخیص کاملاً device-agnostic باقی میماند.
در کنار توکن رفتاری، یک فیلد مخفی هم داریم:
<input name="company" style="display:none" />
کاربر واقعی آن را نمیبیند
باتها معمولاً پرش میکنند
اگر پر باشد، درخواست بدون هیچ بررسی دیگری رد میشود
Human Token در این سیستم:
نه «اثبات انسان بودن» است
نه «مجوز امنیتی»
بلکه:
یک گزارش فشرده از رفتار کاربر است
مثل این که فرانتاند به بکاند بگوید:
«کاربر ۲.۴ ثانیه اینجا بوده، اسکرول کرده، تعامل داشته و صفحه فوکوس داشته.»
const token = await executeHumanCheck("submit-form");
import { useEffect, useRef } from "react"; export function useHumanVerification() { const startedAt = useRef(Date.now()); const interactions = useRef(0); const keyPresses = useRef(0); const focused = useRef(false); useEffect(() => { const inc = () => interactions.current++; window.addEventListener("mousemove", inc); window.addEventListener("touchstart", inc); window.addEventListener("touchmove", inc); window.addEventListener("scroll", inc); window.addEventListener("click", inc); window.addEventListener("keydown", () => { keyPresses.current++; }); window.addEventListener("focus", () => { focused.current = true; }); return () => { window.removeEventListener("mousemove", inc); window.removeEventListener("touchstart", inc); window.removeEventListener("touchmove", inc); window.removeEventListener("scroll", inc); window.removeEventListener("click", inc); }; }, []); const executeHumanCheck = async (action: string): Promise<string> => { const payload = { action, d: Date.now() - startedAt.current, i: interactions.current, k: keyPresses.current, f: focused.current ? 1 : 0, t: Date.now(), }; return btoa(JSON.stringify(payload)); }; return { executeHumanCheck }; }
function ContactForm() { const { executeHumanCheck } = useHumanVerification(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const token = await executeHumanCheck("submit-contact"); await fetch("/api/contact", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Ali", message: "Hello", token, company: "" // honeypot }), }); }; return ( <form ={handleSubmit}> <input name="company" style={{ display: "none" }} /> <input name="name" /> <button type="submit">Send</button> </form> ); }
Honeypot را بررسی کن
Token را decode کن
ساختار را validate کن
thresholdها را بررسی کن
action را match کن
function verifyHumanToken( token: string, expectedAction: string ): boolean { try { const decoded = Buffer.from(token, "base64").toString(); const data = JSON.parse(decoded); if (data.action !== expectedAction) return false; if (data.d < 1200) return false; // خیلی سریع if (data.i < 3) return false; // بدون تعامل if (!data.f) return false; // بدون فوکوس return true; } catch { return false; } }
app.post("/api/contact", (req, res) => { const { token, company } = req.body; if (company) { return res.status(403).send("Bot detected"); } const isHuman = verifyHumanToken(token, "submit-contact"); if (!isHuman) { return res.status(403).send("Bot detected"); } res.send({ success: true }); });
Human Token فقط گزارش رفتار است
فرانتاند فقط جمعآوریکننده است
بکاند تنها مرجع تصمیم است
سیستم بدون CAPTCHA و بدون سرویس خارجی کار میکند