IlmHamroh
JavaScript Full-stack/23-qism. Next.js23/43-dars19 daqiqa
Mundarija (34)

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, curl bilan 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: Origin sarlavhasini 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:

text
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):

ts
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:

ts
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:

text
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.
tsx
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 curl bilan 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:

ts
"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:

tsx
<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

ts
// 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:

ts
// 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:

ts
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:

text
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: true

To'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

ts
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

bash
git add -A
git commit

Commit (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 required atributi —
  • 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 curl bilan 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
js
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:

text
jim
qabul
qabul
limit
xato

Bot 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
js
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:

text
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:

  1. DevTools'da asal idish maydoniga matn yozib yuboring. Javob — "Rahmat", lekin .data/xabarlar.jsonl ga yangi qator qo'shilmaydi.
  2. To'rt marta ketma-ket yuboring. To'rtinchisida — "Juda ko'p urinish".
  3. next.config.ts da serverActions ni experimental dan tashqariga chiqarib npm run tip qiling. 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.ts lar 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: Origin tekshiruvi (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-for faqat ishonchli proksi orqasida.

Keyingi dars: Route Handlers: route.ts bilan API — blog uchun RSS lenta yozamiz va Web standart Request/Response bilan ishlaymiz.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Next.js Server Actions xavfsizligi: ochiq endpoint, CSRF, asal idish va tezlik cheklovi — IlmHamroh