Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Action — ochiq endpoint
- 2.1 Kanon UI'siz chaqirdi
- 2.2 Action ID'ni yashirish — himoya emas
- 3. Next.js'ning o'z himoyalari
- 3.1 CSRF: Origin tekshiruvi
- 3.2 Tana hajmi
- 3.3 Shifrlash va keraksiz action'lar
- 4. Har action ichida: to'rt savol
- 4.1 Auth va ruxsat (28-qismdan keyin)
- 4.2 Qaytgan qiymatni cheklash
- 5. Asal idish (honeypot)
- 5.1 G'oya
- 5.2 Serverda — jim
- 6. Tezlik cheklovi
- 6.1 Kanon tezlik.ts
- 6.2 Natija oynasida: limitni sinash
- 6.3 Action ichida
- 6.4 Halol cheklovlar
- 7. Hujumchi nigohi
- 8. Portfolio qadami: himoya va commit
- 8.1 Fayllar
- 8.2 Tekshiruv
- 8.3 Commit
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): himoyami yoki yo'qmi?
- 2-mashq (o'rta): tekshiruvlar tartibi
- 3-mashq (qiyin): ID va egasi
- 4-mashq: Portfolio qadami
- 11. Real ishda
- Xulosa
- Manbalar
Next.js Server Actions xavfsizligi: ochiq endpoint, CSRF, asal idish va tezlik cheklovi
Qisqacha: Har bir Server Action — internetdagi ochiq POST manzil. Uni formangiz orqali emas,
curlbilan ham chaqirish mumkin, va kanon buni sinab ko'rdi: UI'siz to'g'ridan yuborilgan so'rov qabul qilindi. Next.js o'zi uchta himoya beradi:Originsarlavhasini tekshiradi (CSRF), so'rov hajmini cheklaydi (sukut 1 MB) va action ID hamda closure qiymatlarini shifrlaydi. Qolgani — sizning vazifangiz, har action ichida: kim chaqiryapti (auth), unga ruxsat bormi, ma'lumot to'g'rimi (Zod), qancha tez-tez (tezlik cheklovi), bot emasmi (asal idish). Sahifani yashirish yoki tugmani ko'rsatmaslik — himoya emas.
Bu darsda
- Server Action nega ochiq endpoint ekanini kanonning haqiqiy so'rovlari bilan ko'rasiz.
- Next.js'ning o'z himoyalarini (Origin, tana chegarasi, shifrlash) va ular nimadan himoya qilmasligini bilasiz.
- Har action'da auth, ruxsat va validatsiya tartibini yozasiz.
- Asal idish (honeypot) va xotiradagi tezlik cheklovini yozasiz va jonli sinaysiz.
- Kanonning halol cheklovlarini (
x-forwarded-for, bitta jarayon xotirasi) tushuntirasiz va holat 23 ni commit qilasiz.
Oldin bilishingiz kerak: Server Actions II, Server Actions I, Frontend xavfsizligi React'da, React cache() va DAL, Closure.
1. Nega bu kerak?
Oldingi darsda «Aloqa» formasi tayyor bo'ldi: Zod tekshiradi, xatolar maydonlar yonida. Endi o'zingizni hujumchi o'rniga qo'ying. Sizni forma qiziqtirmaydi — sizni server qiziqtiradi.
DevTools'ni ochasiz, formani bir marta yuborasiz va Network panelida so'rovni ko'rasiz: POST /aloqa, Next-Action sarlavhasi, FormData. Endi bu so'rovni nusxalab (Copy as cURL), skriptda minglab marta takrorlash mumkin. Forma, tugma, brauzer tekshiruvi — hech biri ishtirok etmaydi.
Uyingiz eshigini eslang. Eshik oldiga "kirish taqiqlangan" degan yozuv osish himoya emas — qulf kerak. Sahifada formani ko'rsatmaslik yoki tugmani disabled qilish — yozuv. Action ichidagi tekshiruv — qulf.
Next.js hujjati buni aniq aytadi: "Render-time gating … is not a security boundary, because requests can be sent without going through the UI". Ya'ni sahifani chizishdagi cheklov xavfsizlik chegarasi emas. So'rovni UI'siz yuborish mumkin.
2. Action — ochiq endpoint
2.1 Kanon UI'siz chaqirdi
Kanon «Aloqa» action'ini brauzersiz, to'g'ridan HTTP so'rovlar bilan tekshirdi (xom natija — natijalar/23-forma.txt). Har tekshiruvga alohida x-forwarded-for (IP) berilgan — tezlik cheklovi aralashmasin. Har qatorda ikki narsaga qarang: HTTP status va strelkadan keyingi natija:
4) Origin to'g'ri: 200 → QABUL
4) Origin begona (CSRF): 500 → boshqa (21 bayt)
4) Origin yo'q: 200 → QABUL
4) noto'g'ri ma'lumot (UI'siz): 200 → VALIDATSIYA XATO
4) asal idish to'ldirilgan: 200 → QABUL
4) limit #1 (bir IP): 200 → QABUL
4) limit #2 (bir IP): 200 → QABUL
4) limit #3 (bir IP): 200 → QABUL
4) limit #4 (bir IP): 200 → LIMIT
4) tana 100 KB: 500 → boshqa (21 bayt)Har qatorni darsda ochamiz. Hozir birinchisiga qarang: "Origin to'g'ri: 200 → QABUL". UI ham, brauzer ham yo'q — xabar qabul qilindi. Action haqiqatan ochiq.
flowchart LR
F["Forma (brauzer)"] --> A["POST /aloqa<br/>Next-Action"]
C["curl / skript / bot"] --> A
A --> N["Next.js: Origin, hajm"]
N --> X["xabarYubor: asal idish,<br/>tezlik, Zod, saqlash"]Diagrammadagi ikki kirish nuqtasi bir xil eshikka keladi. Server qaysi biridan kelganini bilmaydi — demak hamma tekshiruv eshikning ichida.
2.2 Action ID'ni yashirish — himoya emas
Action ID shifrlangan va har build'da o'zgaradi. Bu foydali, lekin ID sahifaning o'zida (JS'siz formadagi yashirin maydonda yoki client to'plamida) bor. Formani ko'rgan har kim uni topadi. ID — manzil, parol emas.
3. Next.js'ning o'z himoyalari
3.1 CSRF: Origin tekshiruvi
CSRF (Cross-Site Request Forgery — saytlararo so'rov qalbakilashtirish) — boshqa sayt sizning foydalanuvchingiz brauzeri orqali sizning saytingizga so'rov yuboradigan hujum. Masalan, yomon sayt yashirin forma bilan POST /aloqa qiladi — foydalanuvchi bilmagan holda.
Next.js har action so'rovida Origin sarlavhasini (so'rov qaysi saytdan kelgani) Host bilan solishtiradi. Kanon o'lchovida:
| So'rov | Natija |
|---|---|
Origin — o'z sayti |
200, qabul |
Origin — begona sayt |
500, rad etildi |
Origin yo'q |
200, qabul |
Ikki nozik joy. Birinchisi: rad etish 500 ("Internal Server Error") bilan keladi, 403 emas — loglarda buni server xatosi deb o'ylamang. Ikkinchisi: Origin umuman bo'lmasa — o'tkaziladi. Brauzerlar POST'da Origin ni doim yuboradi, shuning uchun CSRF (begona sayt + foydalanuvchi brauzeri) yopiq. Lekin curl yoki skript Origin siz yuboradi — bunga qarshi CSRF himoyasi ishlamaydi. Bu himoya brauzer orqali hujumdan, to'g'ridan so'rovdan emas.
Saytingiz proksi yoki CDN orqasida boshqa domen bilan ishlasa, ruxsat etilgan manbalar next.config.ts da beriladi (hujjat bo'yicha):
experimental: {
serverActions: {
allowedOrigins: ["portfolio.example.com", "*.example.com"],
},
},3.2 Tana hajmi
Action so'rovining tanasi sukut bo'yicha 1 MB bilan cheklangan. Aloqa xabari 2 000 belgidan oshmaydi — 1 MB ortiqcha. Kanon chegarani kamaytirdi:
const nextConfig: NextConfig = {
// create-next-app 16.4 sukut bo'yicha yoqadi (N-18, N-06)
cacheComponents: true,
partialPrefetching: true,
experimental: {
serverActions: {
// Aloqa xabari 2000 belgi — 1 MB sukut ortiqcha (N-23)
bodySizeLimit: "64kb",
},
},
};O'lchov: 100 KB tana — 500, rad etildi. Katta so'rovlar bilan serverni band qilish qiyinlashdi.
Tuzoq: Next.js qo'llanmasi (guides/server-actions) matnida bu sozlama qisqacha serverActions.bodySizeLimit deb yoziladi — uni next.config.ts ning yuqori darajasiga yozish oson. Aslida u experimental ichida: hujjatdagi kod misoli ham, 16.4.0 ning turi ham shunday. Biz kanon nusxasida yuqori darajaga ko'chirib tsc qildik:
next.config.ts(6,3): error TS2353: Object literal may only specify known properties, and 'serverActions' does not exist in type 'NextConfig'.Tarjimasi: "Obyekt literalida faqat ma'lum xususiyatlar bo'lishi mumkin; NextConfig turida serverActions yo'q". Hujjat matni bilan tur farq qilgandek tuyulsa — tur (haqiqiy kod) ustun. Build chiqishida bu sozlama Experiments (use with caution): · serverActions qatori bilan ko'rinadi.
3.3 Shifrlash va keraksiz action'lar
Hujjat bo'yicha yana ikki himoya bor:
- Ishlatilmagan action'lar client to'plamiga tushmaydi — ular uchun ochiq manzil ham yo'q. Faylga "keyin kerak bo'ladi" deb action yozib qo'yish xavfli emas, lekin import qilinsa — ochiq.
- Closure qiymatlari shifrlanadi. Inline action tashqi o'zgaruvchini ishlatsa (Closure), bu qiymat brauzerga ketadi va qaytib keladi. Next.js uni shifrlab yuboradi.
export default async function Sahifa() {
const versiya = await oxirgiVersiya();
async function chopEt() {
"use server";
// `versiya` brauzerga shifrlangan holda borib, qaytib keladi
if (versiya !== (await oxirgiVersiya())) {
throw new Error("Versiya o'zgargan");
}
}
return <form action={chopEt}>{/* … */}</form>;
}Hujjat ogohlantiradi: shifrlashga yolg'iz tayanmang — maxfiy qiymatni closure'ga umuman olib kirmang. Bir nechta server nusxasi bo'lsa, ular bir xil kalit bilan shifrlashi uchun NEXT_SERVER_ACTIONS_ENCRYPTION_KEY muhit o'zgaruvchisi beriladi (base64 ko'rinishidagi AES kaliti — 16, 24 yoki 32 bayt; Next o'zi 32 baytli yasaydi, masalan openssl rand -base64 32). Kanon — bitta next start, kalit kerak bo'lmadi.
Tekshirib ko'ring: Next.js CSRF'ni tekshiradi. Demak
curlbilan yuborilgan spam xabarlar to'xtaydimi?
Javob
Yo'q. CSRF tekshiruvi Origin ni solishtiradi, curl esa Origin siz yuboradi yoki o'zingizning domeningizni yozadi — ikkala holatda ham o'tadi (kanon: "Origin yo'q: 200 → QABUL"). CSRF — boshqa saytdan foydalanuvchi brauzeri orqali hujumga qarshi. Spam va avtomatlashtirilgan so'rovlarga qarshi — asal idish, tezlik cheklovi va validatsiya.
4. Har action ichida: to'rt savol
Har Server Action boshida, shu tartibda:
| # | Savol | Kanonda | Javob "yo'q" bo'lsa |
|---|---|---|---|
| 1 | Kim? (autentifikatsiya) | Ochiq forma — kerak emas | 401 / kirish sahifasi |
| 2 | Ruxsati bormi? (avtorizatsiya) | — | 403 |
| 3 | Ma'lumot to'g'rimi? | Zod | Maydon xatolari |
| 4 | Juda tez-tezmi? | IP bo'yicha limit | "Keyinroq" xabari |
Arzon tekshiruvlar oldinda: bot (asal idish) va limit Zod'dan oldin — keraksiz ishga resurs sarflanmaydi.
4.1 Auth va ruxsat (28-qismdan keyin)
Portfolio'da login yo'q. Lekin qachondir admin panel qo'shsangiz ("blog postini o'chirish"), action shunday ko'rinadi — Next.js hujjatidagi naqsh asosida, sxema:
"use server";
// sxema — auth 28-qismda
export async function postniOchir(postId: string) {
const sessiya = await joriySessiya(); // 1) kim?
if (!sessiya) throw new Error("Kirish kerak");
const post = await db.post.findFirst({
// 2) o'zinikimi?
where: { id: postId, muallifId: sessiya.foydalanuvchiId },
});
if (!post) throw new Error("Ruxsat yo'q");
await db.post.delete({ where: { id: post.id } });
updateTag("postlar");
}Asosiy qoida hujjatdan: client faqat qaysi yozuv ekanini (ID) va o'zgarishni aytadi. Egasi kimligi, yozuvning qolgan maydonlari — serverdagi ishonchli manbadan (sessiya, baza) qayta o'qiladi. postniOchir({ id, muallifId }) deb butun obyektni client'dan qabul qilish — hujumchiga "men egasiman" deyish imkonini beradi.
Ma'lumot qatlami bilan bog'liqligi: bu tekshiruvlar DAL ichida bo'lsa, har action ularni avtomatik oladi.
4.2 Qaytgan qiymatni cheklash
Action qaytargan har narsa brauzerga ketadi (RSC payload). return post deb bazadagi butun yozuvni qaytarmang — faqat UI ko'rsatadigan narsa (DTO). Kanon xabarYubor faqat { holat, ism } yoki xatolarni qaytaradi — email ham qaytmaydi.
5. Asal idish (honeypot)
5.1 G'oya
Asal idish (honeypot) — odam ko'rmaydigan, lekin bot ko'radigan forma maydoni. Oddiy spam-bot HTML'dagi hamma maydonni to'ldiradi. Odam esa ko'rinmas maydonga hech narsa yozmaydi. Maydon to'la keldi — demak bot.
v1 portfolio'da (04-qism) siz buni HTML va CSS bilan qilgansiz. Kanon uni qaytardi:
<p className="asal-idish" aria-hidden="true">
<label htmlFor={`${id}-veb`}>Veb-sayt (bo'sh qoldiring)</label>
<input
id={`${id}-veb`}
name="veb_sayt"
type="text"
tabIndex={-1}
autoComplete="off"
/>
</p>| Atribut | Kim uchun |
|---|---|
.asal-idish (position: absolute, ekrandan tashqarida) |
Ko'radigan odam |
aria-hidden="true" |
Ekran o'quvchi (Malika) — o'qimaydi |
tabIndex={-1} |
Klaviatura (Bobur) — Tab bilan kirmaydi |
autoComplete="off" |
Brauzerning avto-to'ldirishi |
| Yorliq "bo'sh qoldiring" | Har ehtimolga qarshi — agar biror vosita ko'rsatsa |
display: none emas — ba'zi botlar yashirin maydonlarni tashlab ketadi. Ekrandan tashqariga surish ularni aldaydi.
5.2 Serverda — jim
// Asal idish (honeypot, v1 04/55): odam ko'rmaydi, bot to'ldiradi
// — jim "yuborildi"
if (String(forma.get("veb_sayt") ?? "") !== "")
return { holat: "yuborildi", ism: "" };Bot'ga "Rahmat!" deymiz, lekin hech narsa saqlamaymiz. Nega xato qaytarmaymiz? Xato bot muallifiga "tuzog'ingni topdim" deydi — u maydonni bo'sh qoldirishni o'rganadi. Jim "muvaffaqiyat" esa uni aldaydi. Kanon o'lchovida: "asal idish to'ldirilgan: 200 → QABUL" — javob qabul kabi, lekin .data/xabarlar.jsonl ga yozilmadi.
Asal idish oddiy botlarga qarshi. Aqlli bot (haqiqiy brauzer bilan, CSS'ni hisobga oladigan) uni chetlab o'tadi. Shuning uchun u — himoyaning bitta qavati, yagona emas.
6. Tezlik cheklovi
6.1 Kanon tezlik.ts
Tezlik cheklovi (rate limiting) — bitta manbadan (IP, foydalanuvchi) ma'lum vaqtda ruxsat etilgan so'rovlar soni (backend uchun chuqur — Rate limiting, 24+ qismlar). Kanon — "sirpanuvchi oyna" (sliding window): har kalit uchun oxirgi so'rovlar vaqti saqlanadi, oynadan eskilari tashlanadi:
// Oddiy tezlik cheklovi (N-23): kalit (IP) bo'yicha oynada N ta
// so'rov. Xotirada — bitta server jarayoni uchun; bir nechta
// instansda (Vercel, klaster) har birida alohida — haqiqiy
// yechim Redis (27-qism).
export function tezlikCheklovi(
limit: number,
oynaMs: number,
hozir: () => number = Date.now,
) {
const izlar = new Map<string, number[]>();
return function ruxsatmi(kalit: string): boolean {
const t = hozir();
const yangi = (izlar.get(kalit) ?? []).filter((x) => t - x < oynaMs);
if (yangi.length >= limit) {
izlar.set(kalit, yangi);
return false;
}
yangi.push(t);
izlar.set(kalit, yangi);
return true;
};
}E'tibor bering: hozir — parametr, sukut bo'yicha Date.now. Bu bog'liqlikni kiritish (dependency injection): testda soxta soat beriladi, 10 daqiqani kutish kerak emas (22-qism dagi vi.useFakeTimers o'rniga oddiyroq yo'l).
6.2 Natija oynasida: limitni sinash
Kanon funksiyasini soxta soat bilan ishga tushiramiz — 10 daqiqada 3 ta so'rov:
function rateLimit(
limit: number,
windowMs: number,
now: () => number = Date.now,
) {
const hits = new Map<string, number[]>();
return function allowed(key: string): boolean {
const t = now();
const recent = (hits.get(key) ?? []).filter(
(x) => t - x < windowMs,
);
if (recent.length >= limit) {
hits.set(key, recent);
return false;
}
recent.push(t);
hits.set(key, recent);
return true;
};
}
let time = 0;
const MINUTE = 60_000;
const allowed = rateLimit(3, 10 * MINUTE, () => time);
for (const minute of [0, 1, 2, 3]) {
time = minute * MINUTE;
console.log(`${minute}-daqiqa, IP A:`, allowed("A"));
}
console.log("3-daqiqa, IP B:", allowed("B"));
time = 10 * MINUTE;
console.log("10-daqiqa, IP A:", allowed("A"));Konsolda:
0-daqiqa, IP A: true
1-daqiqa, IP A: true
2-daqiqa, IP A: true
3-daqiqa, IP A: false
3-daqiqa, IP B: true
10-daqiqa, IP A: trueTo'rtinchi so'rov rad etildi — kanon o'lchovidagi "limit #4 (bir IP): 200 → LIMIT" bilan bir xil. Boshqa IP o'z hisobiga ega. 10-daqiqada 0-daqiqadagi so'rov oynadan chiqdi — joy bo'shadi.
6.3 Action ichida
import { headers } from "next/headers";
const ruxsatmi = tezlikCheklovi(3, 10 * 60_000);
// … action ichida, asal idishdan keyin:
const ip =
(await headers()).get("x-forwarded-for")?.split(",")[0]?.trim() ??
"lokal";
if (!ruxsatmi(ip)) {
return {
holat: "xato",
xatolar: {},
umumiy: "Juda ko'p urinish. 10 daqiqadan keyin qayta yozing.",
qiymatlar,
};
}ruxsatmi modul darajasida bir marta yaratiladi — har so'rovda yangi Map bo'lsa, limit hech qachon ishlamasdi. headers() — so'rov sarlavhalari (Request API'lar). Limit xabari umumiy maydonda — formaning tepasidagi role="alert" uni o'qiydi, foydalanuvchi yozganlari esa saqlanadi.
6.4 Halol cheklovlar
Kanon bu yechimning zaif joylarini o'zi yozib qo'ygan:
| Cheklov | Nima uchun | Real yechim |
|---|---|---|
| Xotirada | Server qayta ishga tushsa — hisob nolga | Redis kabi tashqi ombor (27-qism) |
| Bitta jarayon | Vercel'da har nusxa o'z hisobini yuritadi | Umumiy ombor |
x-forwarded-for |
Uni proksi qo'yadi; proksisiz har kim o'zi yozadi | Faqat ishonchli proksi sarlavhasi |
Uchinchisi muhim. Kanon testda aynan shundan foydalangan: har tekshiruvga boshqa x-forwarded-for yozib, limitni chetlab o'tgan. Hujumchi ham shunday qila oladi — agar server oldida bu sarlavhani qayta yozadigan proksi (nginx, Vercel) bo'lmasa. Production'da ishonchli proksi sarlavhani o'zi o'rnatadi va foydalanuvchi yuborganini almashtiradi.
7. Hujumchi nigohi
Endi hammasini birga ko'ramiz. Hujumchi «Aloqa» action'iga nima qila oladi va kanon nima qiladi:
| Hujum | Natija (kanon o'lchovi) | Himoya |
|---|---|---|
| UI'siz POST, to'g'ri ma'lumot | 200 qabul | Bu — ruxsat etilgan amal; qolgan qavatlar spamni cheklaydi |
| Brauzer tekshiruvini o'chirib, noto'g'ri ma'lumot | Zod xatolari | Server validatsiyasi |
mavzu=reklama (UI'da yo'q qiymat) |
Zod xatosi | z.enum |
Ortiqcha maydon (rol=admin) |
E'tiborsiz | Faqat MAYDONLAR o'qiladi |
| Begona saytdan CSRF | 500 rad | Next.js Origin tekshiruvi |
| Spam-bot (hamma maydon) | Jim "Rahmat", saqlanmaydi | Asal idish |
| Bitta IP'dan ko'p yuborish | 4-chisi — limit | Tezlik cheklovi |
| 100 KB tana | 500 rad | bodySizeLimit: "64kb" |
Xabarga <script> |
Saqlanadi, lekin hech qayerda HTML sifatida chiqmaydi | Matn — matn (R-86) |
| IP'ni soxtalashtirib limitni chetlash | Lokalda — o'tadi | Production'da ishonchli proksi |
Oxirgi qator — ochiq qolgan joy, va biz uni yashirmaymiz. Xavfsizlik — qavatlar: har biri bir xil hujumni qimmatroq qiladi, hech biri yolg'iz to'liq emas.
flowchart TB
R["POST /aloqa"] --> O{"Origin to'g'rimi?"}
O -- "yo'q" --> E5["500"]
O -- "ha yoki yo'q sarlavha" --> H{"Asal idish bo'shmi?"}
H -- "yo'q" --> J["jim 'Rahmat'"]
H -- "ha" --> L{"Limit ichidami?"}
L -- "yo'q" --> LM["'Juda ko'p urinish'"]
L -- "ha" --> Z{"Zod"}
Z -- "xato" --> ZX["maydon xatolari"]
Z -- "to'g'ri" --> S["saqlash + 'Rahmat, Aziz!'"]Diagramma — xabarYubor ning to'liq tartibi: arzon tekshiruvlar yuqorida, saqlash — eng oxirida.
8. Portfolio qadami: himoya va commit
Holat 23 ning oxirgi qismi.
8.1 Fayllar
| Fayl | O'zgarish |
|---|---|
src/malumot/tezlik.ts |
Yangi: tezlikCheklovi(limit, oynaMs, hozir) |
aloqa/_amal/xabarYubor.ts |
Asal idish, IP (headers()), limit — Zod'dan oldin |
AloqaFormasi.tsx |
Asal idish maydoni (.asal-idish) |
globals.css |
.asal-idish — ekrandan tashqarida |
next.config.ts |
experimental.serverActions.bodySizeLimit: "64kb" |
8.2 Tekshiruv
Kanon o'lchovining to'liq jadvali (natijalar/23-forma.txt):
| Tekshiruv | Natija |
|---|---|
| JS bilan, brauzer tekshiruvi chetlab o'tilgan | Server xatolari 3 maydon + umumiy, axe 0 |
| JS bilan, to'g'ri | "Rahmat, Aziz! …" |
| JS o'chiq | POST 200 → "Rahmat, Malika! …" |
| To'g'ridan POST, Origin to'g'ri / yo'q | 200 qabul |
| Origin begona | 500 |
| Asal idish to'ldirilgan | 200 jim, saqlanmaydi |
| Bir IP'dan 4 marta | 1–3 qabul, 4-chi LIMIT |
| Tana 100 KB | 500 |
Build: ○ /aloqa — statik qoldi (action — alohida POST, sahifa build vaqtida tayyor). Zod faqat serverda — brauzer JS'i o'zgarmadi. axe 0, hydration 0; lint, tip, format — 0.
8.3 Commit
git add -A
git commitCommit (kanon): Aloqa: Server Action bilan forma — Zod, useActionState, useFormStatus, asal idish, tezlik cheklovi.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| "Faqat adminga ko'rinadi" deb tekshiruvsiz action | Render'dagi cheklov — himoya emas | Auth va ruxsat action ichida |
| Begona Origin'dan 500, sababi noma'lum | Next.js CSRF rad etdi | Proksi domeni — allowedOrigins |
TS2353 … 'serverActions' does not exist |
Sozlama yuqori darajada | experimental.serverActions |
| Katta forma 500 beradi | bodySizeLimit dan oshdi |
Chegarani mos qiling yoki fayllarni boshqa yo'l bilan |
| Limit hech qachon ishlamaydi | tezlikCheklovi har chaqiruvda yangi |
Modul darajasida bir marta |
| Server qayta ishga tushsa limit nolga | Xotiradagi Map |
Tashqi ombor (27-qism) |
| Asal idish ekran o'quvchiga o'qiladi | aria-hidden yo'q |
aria-hidden="true" + tabIndex={-1} |
| Action butun baza yozuvini qaytaradi | Qaytish cheklanmagan | Faqat UI kerak bo'lgan maydonlar |
10. Mashqlar
Mashqlar kurs/mashqlar/23/23-xavfsizlik/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): himoyami yoki yo'qmi?
- Formani faqat kirgan foydalanuvchiga ko'rsatish —
- Action ichida sessiyani tekshirish —
- Inputdagi
requiredatributi — - Serverda Zod
safeParse—
Yechim
- yo'q — action ID'ni bilgan har kim so'rovni UI'siz yuboradi.
- ha — tekshiruv eshik ichida.
- yo'q — DevTools yoki
curlbilan chetlab o'tiladi; bu qulaylik. - ha — server ma'lumotni o'zi tekshiradi.
2-mashq (o'rta): tekshiruvlar tartibi
checkMessage(form, ip, allowed) funksiyasini yozing — kanondagi tartibda: asal idish (veb_sayt to'la → "jim"), limit (allowed(ip) false → "limit"), xabar uzunligi (10 belgidan kam → "xato"), aks holda "qabul". allowed — «Natija oynasida: limitni sinash» dagi funksiya (limit 2). Beshta so'rovni sinang: bot, oddiy ×3 (bitta IP), qisqa xabar (boshqa IP). Bot limitga hisoblanmasligi kerak.
Yechim
function rateLimit(limit, windowMs, now = Date.now) {
const hits = new Map();
return (key) => {
const t = now();
const recent = (hits.get(key) ?? []).filter(
(x) => t - x < windowMs,
);
if (recent.length >= limit) return false;
recent.push(t);
hits.set(key, recent);
return true;
};
}
function checkMessage(form, ip, allowed) {
if (form.veb_sayt) return "jim";
if (!allowed(ip)) return "limit";
if ((form.xabar ?? "").trim().length < 10) return "xato";
return "qabul";
}
const allowed = rateLimit(2, 600_000, () => 0);
const ok = { xabar: "Portfolio juda yoqdi!" };
const bot = { ...ok, veb_sayt: "spam.example" };
console.log(checkMessage(bot, "A", allowed));
console.log(checkMessage(ok, "A", allowed));
console.log(checkMessage(ok, "A", allowed));
console.log(checkMessage(ok, "A", allowed));
console.log(checkMessage({ xabar: "salom" }, "B", allowed));Konsolda:
jim
qabul
qabul
limit
xatoBot asal idishda to'xtadi — limit hisobiga tushmadi. Uchinchi oddiy so'rov — limit (2 ta ruxsat). Qisqa xabar boshqa IP'dan — limitdan o'tdi, validatsiyada to'xtadi. Tartib arzon tekshiruvlarni oldinga qo'yadi.
3-mashq (qiyin): ID va egasi
Vazifalar ro'yxati: { id, matn, egasi }. completeTask(taskId, session) funksiyasini yozing: sessiya bo'lmasa "401"; vazifa topilmasa yoki egasi boshqa bo'lsa — "404" (begona vazifa "mavjud emas" kabi ko'rinsin, borligini oshkor qilmaslik uchun); aks holda vazifani bajarilgan deb belgilab "ok". Client'dan faqat taskId keladi. Uch holatni sinang: sessiyasiz, begona vazifa, o'z vazifasi.
Yechim
const TASKS = [
{ id: "t1", matn: "Portfolio'ni deploy qilish", egasi: "aziz" },
{ id: "t2", matn: "Bozorga borish", egasi: "malika" },
];
function completeTask(taskId, session) {
if (!session) return "401";
const task = TASKS.find(
(t) => t.id === taskId && t.egasi === session.user,
);
if (!task) return "404";
task.bajarildi = true;
return "ok";
}
console.log(completeTask("t1", null));
console.log(completeTask("t2", { user: "aziz" }));
console.log(completeTask("t1", { user: "aziz" }));
console.log(TASKS[0]);Konsolda:
401
404
ok
{
id: 't1',
matn: "Portfolio'ni deploy qilish",
egasi: 'aziz',
bajarildi: true
}Qidiruv id va egasi bo'yicha birga — Next.js hujjatidagi findFirst({ where: { id, ownerId } }) naqshi. 403 o'rniga 404 — begona vazifa borligini ham aytmaymiz. Egasi client'dan kelmaydi, faqat sessiyadan.
4-mashq: Portfolio qadami
tezlik.ts, asal idish va bodySizeLimit ni qo'shing. Keyin npm run dev da uchta tekshiruv:
- DevTools'da asal idish maydoniga matn yozib yuboring. Javob — "Rahmat", lekin
.data/xabarlar.jsonlga yangi qator qo'shilmaydi. - To'rt marta ketma-ket yuboring. To'rtinchisida — "Juda ko'p urinish".
next.config.tsdaserverActionsniexperimentaldan tashqariga chiqaribnpm run tipqiling. TS2353 chiqadi — keyin qaytaring.
Hammasi toza bo'lsa — commit.
Yechim
Namuna — «Kanon tezlik.ts», «Asal idish» va «Tana hajmi» bo'limlaridagi kod. (1) uchun asal idish ekrandan tashqarida — uni DevTools Elements panelida toping va value bering. (2) dev serverda hamma so'rov bitta IP (lokal) — to'rtinchisi limitga tushadi; serverni qayta ishga tushirsangiz hisob nolga qaytadi (xotiradagi limitning halol cheklovi). Commit xabari — kanondagi.
11. Real ishda
- Har Server Action — API endpoint sifatida ko'rib chiqiladi: xavfsizlik auditida ular
route.tslar bilan bir qatorda tekshiriladi. - Rate limiting — production'da Redis (Upstash, Vercel KV) bilan; login, ro'yxatdan o'tish, parolni tiklash, aloqa formalarida majburiy.
- CAPTCHA (Cloudflare Turnstile, hCaptcha) — asal idish yetmaganda keyingi qavat; tashqi skript va maxfiylik narxi bilan (21-qism tajribasi).
- IDOR (begona ID bilan murojaat) — eng ko'p uchraydigan zaiflik turlaridan; "ID va egasi birga" qoidasi uni yopadi.
- Intervyuda: "Server Action xavfsizmi?", "CSRF'dan qanday himoyalanadi?", "Rate limit'ni qanday qilasiz?".
Xulosa
- Har Server Action — ochiq POST endpoint; kanon UI'siz so'rov bilan isbotladi.
- Next.js:
Origintekshiruvi (begona — 500; sarlavhasiz — o'tadi), tana chegarasi (experimental.serverActions.bodySizeLimit), shifrlangan ID va closure. - Har action ichida: kim, ruxsat, validatsiya, tezlik — arzonlari oldinda; ID client'dan, egasi serverdan.
- Asal idish — botga jim "Rahmat"; tezlik cheklovi — sirpanuvchi oyna, soxta soat bilan test qilinadi.
- Kanon cheklovlari halol: xotiradagi limit bitta jarayon uchun,
x-forwarded-forfaqat ishonchli proksi orqasida.
Keyingi dars: Route Handlers: route.ts bilan API — blog uchun RSS lenta yozamiz va Web standart Request/Response bilan ishlaymiz.
Manbalar
- Next.js hujjati — Data Security, Mutating Data 16.4-bob: https://nextjs.org/docs/app/guides/data-security (2026-10-21 da o'qildi)
- Next.js hujjati — Server Actions, Security: https://nextjs.org/docs/app/guides/server-actions#security
- Next.js hujjati —
serverActionssozlamasi: https://nextjs.org/docs/app/api-reference/config/next-config-js/serverActions - OWASP — Cross-Site Request Forgery: https://owasp.org/www-community/attacks/csrf
- Kanon o'lchovlari:
portfoliov2, holat 23 (natijalar/23-forma.txt,tezlik.ts,xabarYubor.ts,next.config.ts); TS2353 — biz ishga tushirdik (typescript 6.0.3, kanon nusxasi).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!