Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. CAPTCHA qanday ishlaydi
- 2.1 Oqim
- 2.2 Uch provayder
- 2.3 Test kalitlari (hujjatdagi)
- 3. Frontend: token holati
- 3.1 To'rt holat
- 3.2 Jonli: o'quv modeli
- 4. Haqiqiy vidjetlar React'da (hujjat bo'yicha)
- 4.1 Turnstile
- 4.2 reCAPTCHA va hCaptcha
- 5. Server tekshiruvi
- 5.1 siteverify
- 5.2 Jonli: javobni to'g'ri o'qish
- 6. Honeypot: ko'rinmas tuzoq
- 6.1 G'oya
- 6.2 To'g'ri yashirish
- 7. Biz sinab ko'rdik: CSP, Trusted Types va maxfiylik
- 7.1 O'lchov
- 7.2 Nima bo'ldi
- 7.3 Ulash uchun nima kerak
- 7.4 Saytda va vazifalar da
- 8. CAPTCHA yolg'iz emas
- 9. Hujumchi nigohi
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): ballga qarab qaror
- 3-mashq (qiyin): muddat tugashi
- 4-mashq: tajriba qadami (ixtiyoriy)
- 12. Real ishda
- Xulosa
- Manbalar
CAPTCHA React'da: Turnstile, reCAPTCHA va hCaptcha — token, server tekshiruvi va honeypot
Qisqacha: CAPTCHA vidjeti foydalanuvchi "odam" ekanini o'z serverida baholaydi va formaga token beradi. Forma tokenni serverga yuboradi, server esa uni maxfiy kalit bilan provayderning
siteverifymanziliga jo'natib tasdiqlatadi — token bir martalik va 5 daqiqalik (Turnstile). Tekshirilmagan token — himoya emas. Har uchala vidjet (Turnstile +2.2 KB, reCAPTCHA +3.5, hCaptcha +6.4) tashqi skript yuklaydi:vazifalardagi Trusted Types ularni to'sdi, CSP'ga provayder hostlari qo'shilishi kerak. Kanon faqat provayderlarning hujjatdagi ochiq test kalitlari bilan o'lchadi; natija oynasida CAPTCHA yo'q — oqim o'quv modelida.
Bu darsda
- CAPTCHA oqimini chizasiz: vidjet → token → forma → server →
siteverify→ qaror. - Turnstile, reCAPTCHA (v2 va v3) va hCaptcha farqini, ularning test kalitlarini bilasiz.
- React formada token holatini boshqarasiz: yo'q, bor, ishlatildi, eskirdi — va vidjetni qayta tiklaysiz.
- Honeypot maydonini to'g'ri yashirasiz va CAPTCHA'ni rate limit bilan birga ishlatasiz.
- CSP, Trusted Types va maxfiylik narxini kanon o'lchovi bilan baholaysiz.
Oldin bilishingiz kerak: Forma xavfsizligi va spamdan himoya, React Hook Form I, Tashqi login va SMS kod, Brauzer xavfsizlik modeli, XSS va DOM xavfsizligi.
1. Nega bu kerak?
O'tgan darsda SMS kod bilan kirishni qurdik va oxirida xavfni aytdik: SMS pumping. Bir kechada Jasur akaga Eskiz'dan xabar keldi: "Balansingiz tugadi". Loglarda — minglab «Kod yuborish» so'rovi, turli raqamlarga, sekundiga o'nlab. Odam bunchalik tez bosmaydi. Bu — skript (bot).
Botlar boshqa joylarda ham: aloqa formasiga reklama, sharhlarga spam, ro'yxatga minglab soxta akkaunt, login formasida parol terish. Ularning umumiy belgisi — avtomatlashtirish.
CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart — kompyuter va odamni ajratadigan avtomatik test) — "siz odammisiz?" degan savol. Eski CAPTCHA'lar qiyshiq harflar yoki "svetoforlarni belgilang" rasmlari edi. Zamonaviylari ko'pincha ko'rinmas: brauzer xatti-harakati, qurilma belgilari va tarmoq obro'si bo'yicha baholaydi, shubhali bo'lsagina vazifa beradi.
Hayotiy o'xshatish: konsertga kirish bilagi (braslet). Kassa (CAPTCHA provayderi) sizni ko'rib, bilak taqadi. Eshikdagi qorovul (sizning serveringiz) bilakni o'zi tekshiradi: haqiqiymi, bugungimi, avval ishlatilmaganmi. Qorovul tekshirmasa, bilakning ma'nosi yo'q — har kim qog'ozdan bilak yasaydi.
2. CAPTCHA qanday ishlaydi
2.1 Oqim
sequenceDiagram
participant B as Brauzer (forma)
participant C as CAPTCHA provayderi
participant S as Bizning server
B->>C: vidjet skripti, sitekey
C->>C: baholash (ko'rinmas yoki vazifa)
C-->>B: token
B->>S: forma + token
S->>C: siteverify: token + maxfiy kalit
C-->>S: success, hostname, error-codes
S-->>B: qabul / radNimaga qarang: ikkita kalit bor.
- Sitekey (sayt kaliti) — ochiq, brauzerda, vidjetga beriladi. Hamma ko'radi — bu normal.
- Secret key (maxfiy kalit) — faqat serverda,
siteverifyga. Brauzerga hech qachon kelmaydi.
Token — vidjet bergan "bilak". Turnstile hujjati bo'yicha: uzunligi 2048 belgigacha, 300 soniya (5 daqiqa) amal qiladi, bir martalik — server uni bir marta tekshira oladi.
2.2 Uch provayder
| Cloudflare Turnstile | Google reCAPTCHA | hCaptcha | |
|---|---|---|---|
| Ko'rinishi | ko'pincha ko'rinmas, kerak bo'lsa — bosish | v2: «Men robot emasman» + rasmlar; v3: ko'rinmas, ball | katakcha + rasmlar |
| Natija | o'tdi / o'tmadi | v2: o'tdi / o'tmadi; v3: ball 0.0–1.0 | o'tdi / o'tmadi |
| Maxfiylik | reklama uchun ishlatmaslikni va'da qiladi | Google cookie'lari | maxfiylikka urg'u beradi |
| Narx | bepul (hisob kerak) | bepul chegara bilan | bepul tarif bor |
reCAPTCHA v3 boshqacha: u vazifa bermaydi, har so'rovga ball qo'yadi (1.0 — aniq odam, 0.0 — aniq bot) va action nomini qaytaradi (masalan "sms_yuborish"). Qaror — sizniki: 0.5 dan past — rad, yoki qo'shimcha tekshiruv (SMS o'rniga email), yoki faqat logga yozish.
Uchala provayder ham hisob talab qiladi — sitekey va secret key hisob ochilganda beriladi. Biz hisob ochmadik. Lekin uchalasi ham hujjatda ochiq test kalitlarini beradi: ishlab chiqish va avtomatik testlar uchun.
2.3 Test kalitlari (hujjatdagi)
Cloudflare Turnstile ("Testing" sahifasi):
| Sitekey | Secret key | Natija |
|---|---|---|
1x00000000000000000000AA |
1x0000000000000000000000000000000AA |
doim o'tadi |
2x00000000000000000000AB |
2x0000000000000000000000000000000AA |
doim o'tmaydi |
1x00000000000000000000AA |
3x0000000000000000000000000000000AA |
"token allaqachon ishlatilgan" (timeout-or-duplicate) |
Yana ikki sitekey: 1x00000000000000000000BB / 2x00000000000000000000BB — ko'rinmas vidjet (o'tadi / o'tmaydi), 3x00000000000000000000FF — doim interaktiv vazifa beradi.
Test sitekey'lar soxta token beradi: XXXX.DUMMY.TOKEN.XXXX. Test secret key'lar faqat shu soxta tokenni qabul qiladi, haqiqiysini — yo'q; production kalitlar esa aksincha. Ya'ni test kalit production'ga "adashib" qolib ketsa — hech kim o'tmaydi, darhol sezasiz.
reCAPTCHA v2 (Google "FAQ"): sitekey 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI, secret 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe — har doim o'tadi, vidjet esa "faqat sinov uchun" ogohlantirishini ko'rsatadi. reCAPTCHA v3 uchun ochiq test kaliti yo'q: Google sinov muhiti uchun alohida kalit ochishni maslahat beradi (v3 balli haqiqiy trafiksiz aniq emas). hCaptcha: sitekey 10000000-ffff-ffff-ffff-000000000001, secret 0x0000000000000000000000000000000000000000, soxta token — 10000000-aaaa-bbbb-cccc-000000000001. (Kalitlar 2026-10 holatiga hujjatdan olindi.)
Bu kalitlar hamma uchun ochiq va hech qanday himoya bermaydi — production'da ishlatilmaydi.
Tekshirib ko'ring: Dasturchi Turnstile test sitekey'i (
1x…AA) bilan production secret key'ni birga ishlatdi. Foydalanuvchilar formani yubora oladimi?
Javob
Yo'q. Test sitekey soxta token (XXXX.DUMMY.TOKEN.XXXX) beradi, production secret key esa soxta tokenni rad etadi. Har yuborish — xato. Kalitlar juft bo'lib almashadi: test — test bilan, production — production bilan. Shuning uchun ikkalasi ham env'dan, bitta muhit sozlamasidan olinadi.
3. Frontend: token holati
3.1 To'rt holat
Formadagi token to'rt holatdan birida:
- Yo'q — vidjet hali baholamadi. Yuborish tugmasi o'chiq (yoki yuborishda xabar).
- Bor — yuborish mumkin.
- Ishlatildi — forma yuborildi. Token boshqa ishlamaydi: xato bo'lib forma qayta yuborilsa, yangi token kerak — vidjet qayta tiklanadi (
reset). - Eskirdi — mehmon formani 5 daqiqadan ko'p to'ldirdi. Vidjet
expiredhodisasini beradi va o'zi yangilaydi yoki bosishni so'raydi.
Eng ko'p uchraydigan xato — 3-holat: server "telefon raqami noto'g'ri" dedi, mehmon tuzatib qayta yubordi — server endi "token ishlatilgan" deydi. Mehmon nima bo'lganini tushunmaydi.
3.2 Jonli: o'quv modeli
Natija oynasida tashqi skript yo'q — haqiqiy vidjet yuklanmaydi. Vidjetni va serverni modellashtiramiz: vidjet 200 ms "o'ylaydi" va token beradi, server esa Turnstile qoidalari bilan tekshiradi — bir martalik va 300 soniya:
import { useEffect, useState } from "react";
// --- O'quv modeli: provayder va bizning server ---
let issued = 0;
const used = new Set();
function widgetSolve() {
// haqiqiy vidjet: provayder skripti, brauzerni baholaydi
return new Promise((resolve) =>
setTimeout(() => {
resolve({ token: `t${++issued}`, at: Date.now() });
}, 200),
);
}
function siteverify(token, at) {
if (!token) {
return { success: false, codes: ["missing-input-response"] };
}
if (used.has(token) || Date.now() - at > 300_000) {
return { success: false, codes: ["timeout-or-duplicate"] };
}
used.add(token); // bir martalik
return { success: true, codes: [] };
}
function serverSendCode({ phone, captcha }) {
const check = siteverify(captcha?.token, captcha?.at);
const verdict = check.success || check.codes[0];
console.log(`server: ${captcha?.token ?? "-"} →`, verdict);
if (!check.success) {
return { ok: false, error: "Tekshiruv o'tmadi" };
}
if (!/^\+998 \d{2} \d{3} \d{2} \d{2}$/.test(phone)) {
return { ok: false, error: "Raqam noto'g'ri" };
}
return { ok: true };
}
// --- Komponent ---
function useCaptcha() {
const [captcha, setCaptcha] = useState(null);
const [round, setRound] = useState(0);
useEffect(() => {
let active = true;
setCaptcha(null);
widgetSolve().then((c) => active && setCaptcha(c));
return () => {
active = false;
};
}, [round]);
return { captcha, reset: () => setRound((r) => r + 1) };
}
export default function App() {
const [phone, setPhone] = useState("");
const [message, setMessage] = useState("");
const { captcha, reset } = useCaptcha();
function submit(e) {
e.preventDefault();
const res = serverSendCode({ phone, captcha });
setMessage(res.ok ? "Kod yuborildi" : res.error);
reset(); // token ishlatildi — keyingi urinishga yangisi
}
return (
<form onSubmit={submit}>
<label htmlFor="tel">Telefon</label>
<input
id="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
<p>
{captcha ? "Tekshiruv: o'tdi" : "Tekshiruv: kutilmoqda…"}
</p>
<button disabled={!captcha}>Kod yuborish</button>
<p role="status">{message}</p>
</form>
);
}Konsolda:
server: t1 → true
server: t2 → trueEkranda:
Telefon
Tekshiruv: o'tdi
Kod yuborish
Kod yuborildiQadamma-qadam:
| Qadam | Token | Server | Ekranda |
|---|---|---|---|
| ochildi | yo'q → 200 ms → t1 |
— | tugma o'chiq, keyin faol |
| «+998 90» yuborildi | t1 ishlatildi |
captcha , raqam | «Raqam noto'g'ri» |
reset() |
yo'q → t2 |
— | tugma qisqa o'chiq |
| to'g'ri raqam | t2 ishlatildi |
«Kod yuborildi» |
Asosiy qator — submit ichidagi reset(). Har yuborishdan keyin, natija qanday bo'lmasin, vidjet yangi token oladi. Uni olib tashlab ko'ring: ikkinchi yuborishda server t1 → timeout-or-duplicate deydi, mehmon esa raqamni tuzatgan bo'lsa ham «Tekshiruv o'tmadi» ni ko'radi.
Haqiqiy vidjetlarda ham shu: Turnstile — turnstile.reset(widgetId), reCAPTCHA — grecaptcha.reset(), hCaptcha — hcaptcha.reset(). React o'ramlarida — ref orqali (turnstileRef.current?.reset()).
4. Haqiqiy vidjetlar React'da (hujjat bo'yicha)
4.1 Turnstile
Kanon @marsidev/react-turnstile 1.6.1 ni o'lchadi (tajriba/kutubxonalar/src/k/captcha-turnstile.tsx — test sitekey bilan). To'liq forma (hujjat bo'yicha, ishga tushirilmagan):
import { Turnstile } from "@marsidev/react-turnstile";
import type { TurnstileInstance } from "@marsidev/react-turnstile";
function SmsForm() {
const [token, setToken] = useState<string | null>(null);
const ref = useRef<TurnstileInstance>(null);
return (
<form onSubmit={async (e) => {
e.preventDefault();
await sendCode({ phone, captchaToken: token });
ref.current?.reset(); // token bir martalik
setToken(null);
}}>
{/* ... telefon maydoni ... */}
<Turnstile
ref={ref}
siteKey={import.meta.env.VITE_TURNSTILE_SITEKEY}
options={{ action: "sms_yuborish", language: "uz" }}
onSuccess={setToken}
onExpire={() => setToken(null)}
onError={() => setToken(null)}
/>
<button disabled={!token}>Kod yuborish</button>
</form>
);
}onSuccess— token keldi.onExpire— 5 daqiqa o'tdi (token yaroqsiz).onError— tarmoq yoki provayder xatosi: mehmonga "Tekshiruv yuklanmadi, sahifani yangilang" va qo'llab-quvvatlash yo'li.action— ixtiyoriy nom; serversiteverifyjavobidagiactionbilan solishtiradi: login formasining tokeni SMS formasida ishlatilmasin.- Sitekey —
VITE_env'dan (ochiq kalit, build'ga tushishi normal). Secret key — faqat server env'ida,VITE_siz.
4.2 reCAPTCHA va hCaptcha
import ReCAPTCHA from "react-google-recaptcha"; // v2 katakcha
<ReCAPTCHA
ref={recaptchaRef}
sitekey={import.meta.env.VITE_RECAPTCHA_SITEKEY}
onChange={setToken} // muddat tugasa — null bilan
/>;
import HCaptcha from "@hcaptcha/react-hcaptcha";
<HCaptcha
ref={hcaptchaRef}
sitekey={import.meta.env.VITE_HCAPTCHA_SITEKEY}
onVerify={setToken}
onExpire={() => setToken(null)}
/>;reCAPTCHA v3 da vidjet yo'q — yuborishdan oldin token so'raladi: grecaptcha.execute(sitekey, { action: "sms_yuborish" }) (hujjat bo'yicha). Google qoidasi: v3 belgisi (badge) ko'rinib tursin yoki uning o'rniga formada matnli eslatma bo'lsin.
5. Server tekshiruvi
5.1 siteverify
Server tokenni o'zi tekshirmaydi — provayderdan so'raydi. Turnstile uchun (hujjat bo'yicha):
// Backend (Node). TURNSTILE_SECRET — faqat server env'ida
const res = await fetch(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
secret: process.env.TURNSTILE_SECRET,
response: token, // formadan kelgan
remoteip: ip, // ixtiyoriy
}),
},
);
const result = await res.json();reCAPTCHA — https://www.google.com/recaptcha/api/siteverify, hCaptcha — https://api.hcaptcha.com/siteverify; g'oya bir xil. To'liq backend — CAPTCHA tokenini serverda tekshirish darsida.
5.2 Jonli: javobni to'g'ri o'qish
success: true — yetarli emas. Javobda yana uch narsa bor: qaysi saytda yechilgan (hostname), qaysi amal uchun (action) va reCAPTCHA v3 da — ball (score). Javob shakli — Cloudflare hujjatidagi namunadan:
function decide(result, expect) {
if (!result.success) {
return `rad: ${result["error-codes"].join(", ")}`;
}
if (result.hostname !== expect.hostname) return "rad: begona sayt";
if (expect.action && result.action !== expect.action) {
return "rad: boshqa amalning tokeni";
}
if (result.score !== undefined && result.score < expect.minScore) {
return `shubhali: ball ${result.score}`;
}
return "qabul";
}
const expect = {
hostname: "example.com",
action: "sms_yuborish",
minScore: 0.5,
};
const ok = {
success: true,
challenge_ts: "2026-10-18T09:14:30.096Z",
hostname: "example.com",
"error-codes": [],
action: "sms_yuborish",
};
console.log(decide(ok, expect));
console.log(decide({ ...ok, action: "login" }, expect));
console.log(decide({ ...ok, hostname: "yomon.example" }, expect));
console.log(
decide(
{ success: false, "error-codes": ["timeout-or-duplicate"] },
expect,
),
);
console.log(decide({ ...ok, score: 0.3 }, expect)); // reCAPTCHA v3Konsolda:
qabul
rad: boshqa amalning tokeni
rad: begona sayt
rad: timeout-or-duplicate
shubhali: ball 0.3hostname— hujumchi tokenni o'z sahifasida (sizning ochiq sitekey'ingiz bilan) yechib, sizning serveringizga yuborishi mumkin. Provayder hostname'ni aytadi — server solishtiradi.action— kam xavfli formada (aloqa) olingan tokenni qimmat formada (SMS) ishlatishga yo'l qo'ymaydi.score— v3 da "rad" emas, "shubhali": ko'pincha qo'shimcha tekshiruv (vazifa, email, kutish).
Xato kodlari (Turnstile hujjatidan): missing-input-response (token yo'q), invalid-input-response (buzuq yoki soxta), timeout-or-duplicate (ishlatilgan yoki eskirgan), invalid-input-secret (secret noto'g'ri). Frontend uchun hammasi bitta xabar: "Tekshiruvdan o'ta olmadingiz, qayta urinib ko'ring" — va vidjetni reset.
Tekshirib ko'ring: Server
siteverifyga so'rov yuborolmadi — Cloudflare 10 soniya javob bermadi. Formani qabul qilasizmi yoki rad etasizmi?
Javob
Bu — biznes qarori, lekin oqibatini biling. Rad etsangiz (fail closed) — provayder ishlamaganda hech kim SMS ololmaydi. Qabul qilsangiz (fail open) — hujumchi provayder yiqilgan paytni kutadi. SMS kabi qimmat amallar uchun odatda rad etiladi, rate limit esa baribir ishlaydi. Arzon amallar (aloqa formasi) uchun qabul qilib, keyinroq ko'rib chiqish mumkin. Qaror loglanadi.
6. Honeypot: ko'rinmas tuzoq
6.1 G'oya
HTML qismida ko'rgan usul: formaga odam ko'rmaydigan, lekin oddiy botlar to'ldiradigan maydon qo'shiladi. To'ldirilgan bo'lsa — yuboruvchi bot. Arzon, foydalanuvchiga hech qanday noqulaylik yo'q, tashqi skript ham yo'q.
Kamchiligi — faqat oddiy botlarni ushlaydi. Formani haqiqiy brauzerda to'ldiradigan bot (puppeteer kabi) ko'rinmas maydonga tegmaydi. Shuning uchun honeypot — birinchi, arzon qatlam; CAPTCHA — ikkinchisi.
6.2 To'g'ri yashirish
import { useState } from "react";
const hidden = {
position: "absolute",
left: "-10000px",
width: "1px",
height: "1px",
overflow: "hidden",
};
function serverAccept(form) {
if (form.website) {
// bot: jim "qabul qilamiz", lekin hech narsa qilmaymiz
console.log("server: honeypot to'ldirilgan — tashlandi");
return { ok: true };
}
console.log("server: qabul —", form.name);
return { ok: true };
}
export default function App() {
const [name, setName] = useState("");
const [website, setWebsite] = useState("");
const [sent, setSent] = useState(0);
function submit(e) {
e.preventDefault();
if (serverAccept({ name, website }).ok) setSent((n) => n + 1);
}
return (
<form onSubmit={submit}>
<p>
<label htmlFor="name">Ism</label>
<input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</p>
<div style={hidden} aria-hidden="true">
<label htmlFor="website">Saytingiz</label>
<input
id="website"
tabIndex={-1}
autoComplete="off"
value={website}
onChange={(e) => setWebsite(e.target.value)}
/>
</div>
<p>
<button>Yuborish</button>
</p>
<p role="status">
{sent > 0 && "Rahmat! Xabaringiz qabul qilindi."}
</p>
</form>
);
}Konsolda:
server: qabul — Aziz
server: honeypot to'ldirilgan — tashlandiEkranda:
Ism
Yuborish
Rahmat! Xabaringiz qabul qilindi.«Saytingiz» ekranda yo'q — u ko'rinish chegarasidan tashqarida. Lekin DOM'da bor: ikkinchi yoz("Saytingiz", …) qadami botni taqlid qildi — maydonni nomi bo'yicha topib to'ldirdi. Server jim tashladi, bot esa xuddi odamdek «Rahmat!» ni ko'rdi.
Har detal nega:
- Ekrandan tashqariga surish,
display: noneemas — ba'zi botlardisplay: nonemaydonlarni o'tkazib yuboradi. tabIndex={-1}— Bobur (faqat klaviatura) Tab bilan unga tushmaydi.aria-hidden="true"— Malikaning ekran o'quvchisi uni o'qimaydi.autoComplete="off"— brauzer avto-to'ldirishi "Saytingiz" maydonini to'ldirib, odamni bot qilib qo'ymasin.- Nomi jozibali (
website,url) — botlar shunday nomli maydonni to'ldirishni yaxshi ko'radi. - Server jim — botga "siz tutildingiz" demaymiz: aks holda bot muallifi maydonni chetlab o'tishni o'rganadi. Bot "Rahmat!" ni ko'radi, lekin xabar hech qayerga ketmaydi.
7. Biz sinab ko'rdik: CSP, Trusted Types va maxfiylik
7.1 O'lchov
Kanon uch vidjetni eng kichik misolda, hujjatdagi test sitekey'lar bilan yig'di va vazifalar CSP'si hamda Trusted Types ostida ochdi; keyin — CSP'siz, qaysi hostlarga borganini yozish uchun. Natija (kutubxonalar-21.txt; CSS va xato ustunlari qisqartirilgan):
captcha-hcaptcha JS 74244 (+ 6.4 KB) | CSP require-trusted-types-for×3,connect-src×1 | ochiq: 48a91d913904.w.hcaptcha.com,6497541344a9.w.hcaptcha.com,api.hcaptcha.com,js.hcaptcha.com,newassets.hcaptcha.com
captcha-recaptcha JS 71312 (+ 3.5 KB) | CSP require-trusted-types-for×1 | ochiq: fonts.gstatic.com,www.google.com,www.gstatic.com
captcha-turnstile JS 69954 (+ 2.2 KB) | CSP require-trusted-types-for×1 | ochiq: challenges.cloudflare.com- Turnstile (@marsidev 1.6.1)2,2 KB
- reCAPTCHA v2 (react-google-recaptcha 3.1.0)3,5 KB
- hCaptcha (@hcaptcha/react-hcaptcha 2.2.0)6,4 KB
Manba: O'lchandi: kanon tajribasi, Vite 8.3.3, React 19.3.0, gzip -9, Chrome 155, natijalar/tajriba/kutubxonalar-21.txt, 2026-10-17
Bu raqamlar — faqat React o'rami. Vidjetning asosiy kodi provayder serveridan yuklanadi va bu hisobga kirmaydi.
7.2 Nima bo'ldi
- Uchalasi ham Trusted Types'da to'xtadi. Xato (Turnstile va reCAPTCHA'da):
This document requires 'TrustedScriptURL' assignment.vaFailed to set the 'src' property on 'HTMLScriptElement'. O'ram provayder skriptini JS'dan<script src=…>yaratib qo'shadi — Trusted Types esascript.srcga oddiy satr yozishni to'sadi (XSS va Trusted Types). Turnstile va reCAPTCHA misolida sahifa matni umuman bo'sh qoldi — xato misoldagi sarlavhani ham chizdirmadi. hCaptcha misolida sarlavha chiqdi, vidjet esa yo'q. - CSP'siz ular tashqi hostlarga bordi — bu hostlar CSP'ga qo'shilishi kerak bo'lgan ro'yxat: Turnstile — bitta (
challenges.cloudflare.com), reCAPTCHA — uchta (www.google.com,www.gstatic.com,fonts.gstatic.com), hCaptcha — beshta (ikkitasi tasodifiy nomli*.w.hcaptcha.com). - hCaptcha CSP ostida yana bir so'rov urindi —
connect-srcbuzilishi:Connecting to 'https://sentry.hcaptcha.com/api/6/envelope/…va konsoldaFailed to send event to Sentry. Vidjet o'z xatosini o'z xato kuzatuvi ga yubormoqchi bo'lgan — sahifangizda uchinchi tomonning uchinchi tomoni.
7.3 Ulash uchun nima kerak
Turnstile uchun (Cloudflare hujjati va kanon hostlari bo'yicha):
script-src 'self' https://challenges.cloudflare.com;
frame-src https://challenges.cloudflare.com;
connect-src 'self' https://challenges.cloudflare.com;Trusted Types uchun — skript manzilini aniq tekshiradigan siyosat. G'oyasi (kanon CAPTCHA bilan sinamagan; PWA darsida SW ro'yxatdan o'tkazgich uchun shunga o'xshash yo'l ishladi):
trustedTypes.createPolicy("captcha", {
createScriptURL(url) {
const u = new URL(url);
if (u.origin === "https://challenges.cloudflare.com") return url;
throw new TypeError(`Ruxsat etilmagan skript: ${url}`);
},
});
// CSP: trusted-types vazifalar captchaMuammo: siyosat nomini kutubxona emas, siz yaratasiz, kutubxona esa uni ishlatmaydi — oddiy satr beradi. Shuning uchun yo default nomli siyosat (hamma joyda ishlaydigan "zaxira" — kengroq ruxsat), yo skriptni HTML'da <script src> bilan o'zingiz ulash (kutubxona uni qayta yuklamasligi kerak). Ikkalasi ham qaror talab qiladi.
Maxfiylik narxi: CAPTCHA skripti sahifa ochilishi bilan mehmonning brauzeri haqida ma'lumot to'playdi — baholash shundan. reCAPTCHA Google cookie'lari bilan ishlaydi. Yevropada bu rozilik talab qilishi mumkin; O'zbekistonda ham shaxsga doir ma'lumot qonuni bor (analitika darsida). Turnstile va hCaptcha maxfiylikni afzallik qilib ko'rsatadi, lekin ular ham uchinchi tomon. Yechim: CAPTCHA'ni har sahifada emas, faqat himoyalanadigan formada va kerak bo'lganda (masalan, «Kod yuborish» bosilganda) yuklash.
7.4 Saytda va vazifalar da
Natija oynasida CAPTCHA'lar yo'q. Ular provayder serveridan skript yuklaydi, natija oynasida esa tarmoq yo'q va CSP qat'iy. vazifalar da ham yo'q: ochiq ro'yxat yoki SMS formasi yo'q (kirish — soxta), Trusted Types esa uchalasini to'sdi. Backend bilan haqiqiy kirish paydo bo'lganda (28-qism) — CAPTCHA kerakli formada, yuqoridagi CSP qarori bilan.
8. CAPTCHA yolg'iz emas
CAPTCHA botni to'xtatmaydi — uning ishini qimmatlashtiradi. CAPTCHA yechib beradigan pullik xizmatlar va yollangan odamlar bor: minglab yechim arzon narxda. Agar bitta SMS'ning narxi CAPTCHA yechimidan qimmat bo'lsa, hujumchiga baribir foydali.
Shuning uchun qatlamlar:
flowchart TD
A["So'rov: «Kod yuborish»"] --> B{"Honeypot<br/>bo'shmi?"}
B -- yo'q --> X["jim tashlash"]
B -- ha --> C{"CAPTCHA token<br/>siteverify"}
C -- o'tmadi --> R["rad"]
C -- o'tdi --> D{"Rate limit:<br/>IP, raqam, vaqt"}
D -- oshdi --> L["429 — keyinroq"]
D -- ok --> E{"Raqam +998 mi?"}
E -- yo'q --> R
E -- ha --> S["SMS yuboriladi"]Nimaga qarang: CAPTCHA — to'rt qatlamdan biri. Rate limit (bir IP'dan, bir raqamga, bir daqiqada nechta) — eng muhimi va u faqat serverda (Rate limiting). Mamlakat cheklovi — SMS pumping'ning asosiy davosi: xorijiy premium raqamlarga SMS umuman ketmaydi.
Accessibility ham qatlam qarori: rasmli vazifalar (svetoforlar) ko'r va ko'zi zaif foydalanuvchilar uchun to'siq. Ko'rinmas usullar (Turnstile, reCAPTCHA v3) bu jihatdan yaxshiroq, lekin baribir ba'zan vazifa beradi. Malika uchun muqobil yo'l bo'lsin — masalan, qo'llab-quvvatlash orqali.
9. Hujumchi nigohi
1. Server tekshirmaydi. Eng keng tarqalgan xato: frontend "token bor" deb tugmani yoqadi, server esa tokenni olib, siteverify qilmaydi. Hujumchi brauzerni umuman ochmaydi — curl bilan captchaToken: "x" yuboradi. Natija: CAPTCHA faqat halol foydalanuvchiga xalaqit beradi.
2. Token qayta ishlatilishi. Bitta haqiqiy token bilan yuzta so'rov. Provayder buni ushlaydi (timeout-or-duplicate) — lekin faqat har so'rovda siteverify chaqirilsa. "Bir marta tekshirib, keshlash" — xato.
3. Begona sayt tokeni. Sitekey ochiq: hujumchi uni o'z sahifasiga qo'yib, tokenlarni "fermada" yig'adi. hostname (va action) tekshiruvi buni yopadi.
4. Secret key sizib chiqishi. VITE_TURNSTILE_SECRET — build'ga tushadi. Kim o'qisa, o'zi siteverify qila oladi va kalitingizni suiiste'mol qiladi. Secret — faqat server env'ida.
5. CAPTCHA'ni chetlab o'tish. Bitta formada CAPTCHA bor, xuddi shu amalni bajaradigan boshqa API manzilida (mobil ilova uchun eski /api/v1/sms) — yo'q. Hujumchi shu eshikdan kiradi. Himoya amal bo'yicha bo'lsin, sahifa bo'yicha emas.
10. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Ikkinchi yuborishda «Tekshiruv o'tmadi» | token bir martalik, vidjet tiklanmagan | har yuborishdan keyin reset() |
| 5 daqiqadan keyin forma o'tmaydi | token eskirdi | onExpire → token null, vidjet yangilaydi |
| Hech kim o'tmaydi (production) | test sitekey + production secret (yoki aksi) | kalitlar juft, bitta env'dan |
This document requires 'TrustedScriptURL' assignment. |
o'ram skriptni JS'dan yuklaydi, Trusted Types | TT siyosati yoki <script> HTML'da |
Refused to load the script 'https://challenges.cloudflare.com/…' |
CSP script-src da host yo'q |
provayder hostlari CSP'ga |
| Bot baribir o'tadi | server siteverify qilmaydi yoki hostname ni tekshirmaydi |
har so'rovda tekshiruv + hostname/action |
| Klaviatura bilan honeypot'ga tushiladi | tabIndex yo'q |
tabIndex={-1}, aria-hidden |
11. Mashqlar
Mashqlar kurs/mashqlar/21/25-captcha/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): bo'sh joylarni to'ldiring
- Brauzerga beriladigan ochiq kalit:
- Server tokenni tekshirish uchun murojaat qiladigan provayder manzili (nomi):
- Turnstile tokeni necha soniya amal qiladi:
- reCAPTCHA v3 qaytaradigan, 0.0 dan 1.0 gacha qiymat:
- Ishlatilgan yoki eskirgan token uchun Turnstile xato kodi:
Yechim
Sitekey — ochiq, secret key — faqat serverda. siteverify — provayderning tekshiruv API'si. Turnstile tokeni 300 soniya va bir martalik. v3 score — odamlik ehtimoli, chegarani o'zingiz qo'yasiz. timeout-or-duplicate — frontend uchun signal: vidjetni reset qiling.
2-mashq (o'rta): ballga qarab qaror
reCAPTCHA v3 balli bo'yicha uch xil javob beradigan riskLevel(score, action) funksiyasini yozing:
action—"sms_yuborish"bo'lmasa —"rad";score >= 0.7—"o'tkazish";0.3 <= score < 0.7—"qo'shimcha tekshiruv"(masalan, rasmli vazifa);score < 0.3—"rad".
Beshta holat bilan sinang: 0.9, 0.7, 0.5, 0.1 (hammasi to'g'ri action bilan) va 0.9 + "login". Natija: ball → qaror qatorlari.
Ishora: shartlar tartibi — avval action, keyin balldan yuqoridan pastga (if / else if / else).
Yechim
function riskLevel(score, action) {
if (action !== "sms_yuborish") return "rad";
if (score >= 0.7) return "o'tkazish";
if (score >= 0.3) return "qo'shimcha tekshiruv";
return "rad";
}
const cases = [
[0.9, "sms_yuborish"],
[0.7, "sms_yuborish"],
[0.5, "sms_yuborish"],
[0.1, "sms_yuborish"],
[0.9, "login"],
];
for (const [score, action] of cases) {
console.log(`${score} (${action}) → ${riskLevel(score, action)}`);
}Konsolda:
0.9 (sms_yuborish) → o'tkazish
0.7 (sms_yuborish) → o'tkazish
0.5 (sms_yuborish) → qo'shimcha tekshiruv
0.1 (sms_yuborish) → rad
0.9 (login) → radOxirgi holat — yuqori ball, lekin begona amal: login formasining tokeni SMS uchun ishlamaydi. Chegaralar (0.7, 0.3) — misol uchun. Haqiqiy loyihada ular loglardan tanlanadi: bir hafta faqat yozib boriladi, keyin odamlar va botlarning ballari solishtiriladi.
3-mashq (qiyin): muddat tugashi
«Jonli: o'quv modeli» ni kengaytiring: o'quv modelida token muddati 300 soniya emas, 300 ms.
useCaptchatoken kelgandan 300 ms keyin uni o'zinullqilsin va konsolgatoken eskirdiyozsin (haqiqiy vidjetdagionExpire), keyin avtomatik yangisini olsin.- Ekranda «Tekshiruv: o'tdi» / «Tekshiruv: kutilmoqda…».
Sinov: hech narsa bosmay 600 ms kuting — konsolda tokenlar va eskirish qatorlari, oxirida yangi token bilan «o'tdi».
Ishora: token kelgach setTimeout(…, 300); effekt cleanup'ida taymerni tozalang (cleanup).
Yechim
import { useEffect, useState } from "react";
const TTL = 300; // o'quv modeli: 300 ms (haqiqiysi — 300 soniya)
let issued = 0;
function widgetSolve() {
return new Promise((resolve) =>
setTimeout(() => resolve(`t${++issued}`), 100),
);
}
function useCaptcha() {
const [token, setToken] = useState(null);
const [round, setRound] = useState(0);
useEffect(() => {
let active = true;
let expiry;
setToken(null);
widgetSolve().then((t) => {
if (!active) return;
console.log("token:", t);
setToken(t);
expiry = setTimeout(() => {
console.log("token eskirdi:", t);
setRound((r) => r + 1); // yangisini olish
}, TTL);
});
return () => {
active = false;
clearTimeout(expiry);
};
}, [round]);
return token;
}
export default function App() {
const token = useCaptcha();
return (
<p>{token ? "Tekshiruv: o'tdi" : "Tekshiruv: kutilmoqda…"}</p>
);
}Konsolda:
token: t1
token eskirdi: t1
token: t2Ekranda:
Tekshiruv: o'tdiVaqt chizig'i: 100 ms — t1, 400 ms — eskirdi va yangi so'rov, 500 ms — t2, 700 ms da tekshiruv tugadi (t2 800 ms gacha yaroqli). Haqiqiy Turnstile buni o'zi qiladi (refresh-expired: "auto" sukut), siz faqat onExpire da tugmani o'chirasiz.
4-mashq: tajriba qadami (ixtiyoriy)
vazifalar ga tegmang. Alohida papkada (kurs/tajriba/captcha/) Vite + React loyiha oching, @marsidev/react-turnstile ni o'rnating va faqat hujjatdagi test kalitlari bilan forma yasang (1x00000000000000000000AA). Keyin:
- Vidjet chiqdimi,
onSuccessqanday token berdi? index.htmlgavazifalardagi CSP<meta>i qo'shilganda konsolda nima bo'ldi?- Sitekey'ni
2x00000000000000000000ABga almashtiring — vidjet nima ko'rsatdi?
Hisob ochmang, haqiqiy kalit olmang.
Yechim
- Vidjet chiqadi va hujjatdagi soxta token keladi:
XXXX.DUMMY.TOKEN.XXXX. - Trusted Types va CSP buzilishlari — kanon natijasidagi kabi:
This document requires 'TrustedScriptURL' assignment., vidjet chizilmaydi. «Ulash uchun nima kerak» dagi CSP qatorlari va TT qarori bilan qayta sinang. - Test sitekey "doim o'tmaydi" — vidjet xato holatini ko'rsatadi va
onError(yoki token kelmasligi) — formangiz bu holatda nima deyishini tekshiring.
Brauzer xabarlarining aniq matni versiyaga qarab farq qilishi mumkin — kanon o'lchovi Chrome 155 da.
12. Real ishda
- Ro'yxat, login, parolni tiklash, SMS kod, aloqa formasi, sharhlar — CAPTCHA odatda shu formalarda. Ko'p saytlar uni doim emas, shubha paydo bo'lganda ko'rsatadi: bir IP'dan ko'p urinish yoki v3 balli past.
- Cloudflare ortidagi saytlar ko'pincha Turnstile'ni tanlaydi (bitta hisob, maxfiylik). Google ekotizimidagi loyihalar — reCAPTCHA (Enterprise).
- Testlarda — provayderlarning test kalitlari: e2e testlar (22-qism) haqiqiy CAPTCHA'ni yecha olmaydi, test kaliti doim o'tadi.
- Intervyu savollari: "CAPTCHA tokeni qayerda tekshiriladi va nega?", "Honeypot qanday ishlaydi?", "reCAPTCHA v2 va v3 farqi?", "CAPTCHA SMS pumping'dan to'liq himoya qiladimi?", "CAPTCHA'ni qat'iy CSP bilan qanday ulaysiz?".
Xulosa
- CAPTCHA oqimi: vidjet (sitekey) → token → forma → server
siteverify(secret key) → qaror; token bir martalik va qisqa muddatli — har yuborishdan keyinreset. - Server javobida
successyetmaydi:hostname,actionva v3 dascoreham tekshiriladi; provayder javob bermasa — oldindan qaror (fail open/closed). - Test kalitlari — hujjatda ochiq (Turnstile
1x…AA→XXXX.DUMMY.TOKEN.XXXX); faqat test bilan, juft holda. - Honeypot — arzon birinchi qatlam: ekrandan tashqarida,
tabIndex={-1},aria-hidden, server jim tashlaydi; CAPTCHA — ikkinchi, rate limit va mamlakat cheklovi — asosiysi. - Kanon o'lchovi: Turnstile +2.2 KB, reCAPTCHA +3.5, hCaptcha +6.4 — uchalasi Trusted Types'da to'xtadi (
TrustedScriptURL); CSP'ga 1, 3 va 5 host; maxfiylik narxi — uchinchi tomon skripti.
Keyingi dars: AI chat interfeyslari: oqimli javoblar — javob so'zma-so'z keladi: fetch va SSE oqimini o'qish, «To'xtatish» va «Qayta yaratish», vazifalar dagi yordamchi.
Manbalar
- Cloudflare Turnstile: "Get started", "Server-side validation" (siteverify, xato kodlari, token xususiyatlari), "Testing" (test kalitlari, soxta token), "Content Security Policy" — developers.cloudflare.com/turnstile
- Google reCAPTCHA: "reCAPTCHA v2", "reCAPTCHA v3" (score, action), "Verifying the user's response", "FAQ" (test kalitlari) — developers.google.com/recaptcha
- hCaptcha: "Developer Guide" (test kalitlari, siteverify) — docs.hcaptcha.com
@marsidev/react-turnstile— docs.page/marsidev/react-turnstile;react-google-recaptcha,@hcaptcha/react-hcaptcha— npm- MDN: Trusted Types API, CSP
script-src,frame-src— developer.mozilla.org - Kanon:
tajriba/kutubxonalar/src/k/captcha-turnstile.tsx,captcha-recaptcha.tsx,captcha-hcaptcha.tsx,natijalar/tajriba/kutubxonalar-21.txtva.json(2026-10-17)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!