Mundarija (23)
- Bu darsda
- 1. Nega bu kerak?
- 2. Set-Cookie sarlavhasi
- 2.1 Simdagi ko'rinish
- 2.2 Atributlar xaritasi
- 3. Kanon tajribasi: 14 ta cookie haqiqiy brauzerda
- 3.1 Qaysilari saqlandi
- 3.2 Brauzer nimani rad etadi
- 4. SameSite: sayt va origin
- 4.1 Ikki tushuncha
- 4.2 Kanon o'lchovi: beshta yo'l
- 5. Imzolangan cookie
- 6. Uchinchi tomon cookie'lari va CHIPS
- 7. Hujumchi nigohi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): to'g'ri yoki noto'g'ri
- 2-mashq (o'rta): Set-Cookie yasovchi
- 3-mashq (qiyin): SameSite simulyatori
- 10. Loyiha qadami: cookieOl() va COOKIE_SECURE
- 11. Real ishda
- Xulosa
- Manbalar
Cookie chuqur: HttpOnly, Secure, SameSite va __Host- prefiksi
Qisqacha: Cookie — brauzer saqlaydigan va har so'rovga o'zi qo'shadigan kichik yozuv. Uning xavfsizligi atributlarda:
HttpOnly— JavaScript o'qiy olmaydi,Secure— faqat HTTPS orqali,SameSite— boshqa saytdan kelgan so'rovga qo'shilmaydi,__Host-prefiksi — subdomen soxtalashtira olmaydi. Kanon 14 ta cookie'ni haqiqiy Chromium'da sinadi: 10 tasi saqlandi, 4 tasini brauzer rad etdi. Eng kutilmagan natija: SameSite bir xil host'ning boshqa portidan kelgan hujumni to'xtatmaydi — port "sayt" tushunchasiga kirmaydi.
Bu darsda
Set-Cookiesarlavhasini o'qiysiz va har atributning vazifasini tushuntirasiz.- "Sayt" va "origin" farqini bilib,
SameSiteqachon himoya qilishini aytasiz. - Brauzer qaysi cookie'ni rad etishini oldindan aytib bera olasiz (
__Host-,SameSite=None, 4096 bayt). - Imzolangan cookie'ni Web Crypto HMAC bilan yasaysiz va tekshirasiz.
vazifalar-apigacookieOl()yordamchisini qo'shasiz.
Oldin bilishingiz kerak: HTTP sarlavhalari, MIME turlari, cookie va kesh, Cookie'lar JS'dan, res obyekti, crypto 1: tasodif, hash va HMAC, Ro'yxat va login oqimi.
Oldingi darsda login "ha, bu Aziz" deyishni o'rgandi. Lekin HTTP xotirasiz: keyingi so'rovda server Azizni tanimaydi. Brauzer har so'rov bilan "men — Aziz" degan isbotni olib kelishi kerak. Bu isbot odatda cookie ichida yuradi. Shuning uchun sessiyani qurishdan oldin cookie'ni chuqur bilishimiz kerak.
1. Nega bu kerak?
Cookie'ni 2- va 11-qismlarda ko'rgansiz: server Set-Cookie yuboradi, brauzer saqlaydi, keyingi so'rovlarga Cookie sarlavhasi bilan qo'shadi. document.cookie bilan JS ham o'qiy oladi.
Auth uchun bu "o'zi qo'shiladi" xususiyati ham kuch, ham xavf:
- Kuch: frontend tokenni saqlash va har
fetchga qo'shish haqida o'ylamaydi. - Xavf: brauzer cookie'ni boshqa sayt boshlagan so'rovga ham qo'shishi mumkin. Hujumchi saytidagi forma sizning API'ingizga POST yuboradi — brauzer esa Azizning cookie'sini "sadoqat bilan" qo'shib beradi. Bu CSRF (Cross-Site Request Forgery) hujumining asosi.
Ikkala tomon bitta diagrammada. Uchinchi strelkaga qarang: hujumchi sahifasi boshlagan so'rovga ham brauzer cookie'ni o'zi qo'shmoqchi bo'ladi.
sequenceDiagram
participant B as Brauzer
participant A as vazifalar-api
participant H as Hujumchi sahifasi
A-->>B: Set-Cookie: sessiya=...
B->>A: GET /v1/vazifalar + Cookie
H->>B: yashirin forma POST
B->>A: POST /v1/vazifalar + Cookie?So'roq belgisi bejiz emas: cookie qo'shiladimi yo'qmi — atributlarga bog'liq. Atributlar aynan shu ikki tomonni boshqaradi. Noto'g'ri bitta atribut — va sessiya XSS bilan o'g'irlanadi yoki boshqa saytdan ishlatiladi.
2. Set-Cookie sarlavhasi
2.1 Simdagi ko'rinish
Server cookie'ni javob sarlavhasi bilan beradi. Har cookie — alohida Set-Cookie qatori:
import { createServer } from "node:http";
const server = createServer((req, res) => {
res.setHeader("Set-Cookie", [
"sessiya=abc123; Path=/; HttpOnly; Secure; SameSite=Lax",
"til=uz; Path=/; Max-Age=31536000",
]);
res.end("ok");
});
await new Promise((r) => server.listen(0, "127.0.0.1", r));
const { port } = server.address();
const javob = await fetch(`http://127.0.0.1:${port}/`);
for (const c of javob.headers.getSetCookie()) console.log(c);
console.log(javob.headers.get("set-cookie"));
server.closeAllConnections();
server.close();Konsolda:
sessiya=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
til=uz; Path=/; Max-Age=31536000
sessiya=abc123; Path=/; HttpOnly; Secure; SameSite=Lax, til=uz; Path=/; Max-Age=31536000Birinchi ikki qator — getSetCookie(): har cookie alohida. Uchinchisi — oddiy get(): ikkalasini vergul bilan qo'shib yuboradi. Bu xavfli, chunki Expires=Thu, 26 Nov 2026 … sanasida ham vergul bor — bunday satrni to'g'ri bo'lib bo'lmaydi. Shuning uchun Set-Cookie uchun maxsus getSetCookie() metodi bor. Bu blok faqat Node'da ishlaydi — saytda «Ishga tushir» tugmasi chiqmaydi, o'z terminalingizda yurgizing.
2.2 Atributlar xaritasi
nom=qiymat dan keyin ; bilan atributlar keladi. Brauzer ularni o'zida saqlaydi va serverga qaytarmaydi: Cookie sarlavhasida faqat nom=qiymat boradi.
| Atribut | Nima qiladi | Xavfsizlikdagi o'rni |
|---|---|---|
HttpOnly |
document.cookie da ko'rinmaydi |
XSS skripti cookie'ni o'g'irlay olmaydi |
Secure |
faqat HTTPS orqali yuboriladi | tarmoqda ochiq yurmaydi |
SameSite |
boshqa saytdan boshlangan so'rovga qo'shiladimi | CSRF'ga qarshi birinchi qatlam |
Domain |
qaysi host(lar)ga yuboriladi | qo'yilsa — subdomenlarga ham ketadi |
Path |
qaysi yo'llarga yuboriladi | xavfsizlik chegarasi emas |
Max-Age / Expires |
qachon o'chadi | yo'q bo'lsa — brauzer yopilganda |
Max-Age soniyada, Expires — aniq sana. Ikkalasi bo'lsa, Max-Age ustun. Hech biri bo'lmasa — sessiya cookie'si: brauzer yopilganda o'chadi (lekin "oldingi oynalarni tiklash" yoqilgan brauzer uni saqlab qolishi mumkin).
3. Kanon tajribasi: 14 ta cookie haqiqiy brauzerda
3.1 Qaysilari saqlandi
Kanon node:http serverini ko'tarib, bitta javobda 14 ta Set-Cookie yubordi va Chromium nimani saqlaganini o'qidi. Brauzer — Playwright o'rnatgan Chromium headless shell 151.0.7922.34, server http://localhost:4340, TLS yo'q. Natijadan (o'lchov jurnalidan aynan):
=== 1) http://localhost:4340/qoy — 14 ta Set-Cookie dan brauzer saqlagani (10):
__Host-sid[S][H][Lax] __Secure-s[S][Lax] api_yol[Lax] httponly[H][Lax] lax[Lax] none[S][None] qisqa[Lax] secure[S][Lax] strict[Strict] sukut[Lax]
rad etilganlar: __Host-secsiz, __Host-domen, none_secsiz, katta
document.cookie (HttpOnly ko'rinmaydi): __Secure-s,lax,none,qisqa,secure,strict,sukut
1.2 s dan keyin qisqa (Max-Age=1): o'chdi
/korsat (oz-sayti) yuborildi: __Host-sid,__Secure-s,httponly,lax,none,secure,strict,sukut
/api/korsat (oz-sayti-api) yuborildi: __Host-sid,__Secure-s,api_yol,httponly,lax,none,secure,strict,sukut[S] — Secure, [H] — HttpOnly, qavsdagi so'z — SameSite. Qatorma-qator o'qiymiz:
sukut[Lax]—SameSiteyozilmagan cookie'ga Chromium o'ziLaxqo'ydi. Bu "Lax-by-default" xulqi.- Rad etilgan to'rtta — keyingi bo'limda sababi bilan.
document.cookieda__Host-sidvahttponlyyo'q —HttpOnlyishladi.api_yolham yo'q: uPath=/apibilan, sahifa esa/qoyda.Max-Age=1— 1.2 soniyadan keyin o'chdi./korsatgaapi_yolbormadi,/api/korsatga bordi —Pathishladi.qisqaikkalasida ham yo'q — u allaqachon o'chgan edi.
Eng muhim kuzatuv sarlavhada yashiringan: server http da, TLS yo'q. Shunga qaramay Secure va __Host- cookie'lar saqlandi. Kanon xuddi shu sinovni http://127.0.0.1:4340 da ham qildi — natija aynan bir xil. Chromium localhost va 127.0.0.1 ni "ishonchli" origin deb biladi va ularda Secure ni HTTPS'siz ham qabul qiladi. Bu dasturchi uchun qulay: lokal kompyuterda ham prodakshndagi atributlar bilan ishlash mumkin. LAN IP (192.168.…) yoki oddiy http domen esa bunday imtiyozga ega emas.
3.2 Brauzer nimani rad etadi
Rad etish qoidalarini kodga aylantiramiz va kanon ro'yxatining bir qismida sinaymiz:
function tahlil(sarlavha) {
const [juft, ...qolgan] = sarlavha.split(";").map((s) => s.trim());
const nom = juft.slice(0, juft.indexOf("="));
const atr = new Map();
for (const q of qolgan) {
const [k, v = ""] = q.split("=");
atr.set(k.toLowerCase(), v);
}
return { nom, atr, hajm: new TextEncoder().encode(juft).length };
}
function radSababi({ nom, atr, hajm }) {
if (hajm > 4096) return "4096 baytdan katta";
const ss = atr.get("samesite")?.toLowerCase();
if (ss === "none" && !atr.has("secure")) return "None — Secure'siz";
if (nom.startsWith("__Secure-") && !atr.has("secure")) {
return "__Secure- — Secure'siz";
}
if (nom.startsWith("__Host-")) {
if (!atr.has("secure")) return "__Host- — Secure'siz";
if (atr.has("domain")) return "__Host- — Domain bilan";
if (atr.get("path") !== "/") return "__Host- — Path=/ emas";
}
return null;
}
const QOY = [
"__Host-sid=1; Path=/; Secure; HttpOnly; SameSite=Lax",
"__Host-secsiz=1; Path=/; HttpOnly",
"__Host-domen=1; Path=/; Secure; Domain=localhost",
"none=1; Path=/; SameSite=None; Secure",
"none_secsiz=1; Path=/; SameSite=None",
"sukut=1; Path=/",
`katta=${"x".repeat(5000)}; Path=/`,
];
for (const s of QOY) {
const c = tahlil(s);
console.log(c.nom.padEnd(14), radSababi(c) ?? "saqlanadi");
}Konsolda:
__Host-sid saqlanadi
__Host-secsiz __Host- — Secure'siz
__Host-domen __Host- — Domain bilan
none saqlanadi
none_secsiz None — Secure'siz
sukut saqlanadi
katta 4096 baytdan kattaBizning kichik funksiyamiz haqiqiy Chromium bilan bir xil to'rtta cookie'ni rad etdi. Bu qoidalarning har biri bejiz emas:
__Host-prefiksi — brauzerga va'da: "bu cookie faqat shu host'dan,Securebilan,Path=/vaDomainsiz qo'yilgan". Nega kerak? Tasavvur qiling,ilova.example.comning sessiya cookie'si bor. Hujumchiblog.example.comsubdomenini buzdi. Oddiy cookie bo'lsa, uDomain=example.combilan o'z sessiya cookie'sini qo'yib, qurbonnikini "soya"da qoldira oladi.__Host-nomli cookie'ni esaDomainbilan qo'yib bo'lmaydi — brauzer rad etadi.__Secure-— yumshoqroq: faqatSecuretalab qiladi.SameSite=NonefaqatSecurebilan — boshqa saytlarga ochiq cookie hech bo'lmaganda shifrlangan kanalda yursin.- 4096 bayt — RFC 6265 brauzerdan kamida shuncha hajmni qo'llashni talab qiladi; Chromium undan kattasini saqlamadi.
Tekshirib ko'ring: Nega
__Host-cookie'daPath=/majburiy?Path=/adminbilan qo'yilsa nima yomon?
Javob
Path — xavfsizlik chegarasi emas, faqat "qaysi yo'llarga yuboraman" degan filtr. Bir host'dagi /blog sahifasining skripti /admin cookie'sini ham yashirin iframe orqali o'qishi mumkin. __Host- "butun host uchun bitta cookie" g'oyasiga asoslangan: Path=/ bo'lsa, boshqa yo'lda xuddi shu nomli ikkinchi cookie yaratib, birinchisini chalkashtirib bo'lmaydi.
4. SameSite: sayt va origin
4.1 Ikki tushuncha
- Origin — sxema + host + port:
http://localhost:5173vahttp://localhost:3000— ikki xil origin. CORS shu bilan ishlaydi. - Sayt (site) — sxema + "ro'yxatdan o'tgan domen" (eTLD+1):
example.com,ilmhamroh.uz. Port kirmaydi, subdomen ham kirmaydi.localhost:5173valocalhost:3000— bitta sayt.
SameSite origin'ga emas, saytga qaraydi. "Boshqa saytdan" degani — so'rovni boshqa ro'yxatdan o'tgan domendagi sahifa boshladi.
| Qiymat | Boshqa saytdan boshlangan so'rovga qo'shiladimi |
|---|---|
Strict |
hech qachon (havola bosilsa ham) |
Lax |
faqat yuqori darajadagi xavfsiz navigatsiyada (havola, GET) |
None |
har doim (Secure majburiy) |
4.2 Kanon o'lchovi: beshta yo'l
Kanon cookie'larni localhost:4340 da qo'ydi va ikki "hujum" sahifasini ochdi: boshqa saytdagi (127.0.0.1:4340 — host boshqa) va bir saytning boshqa portidagi (localhost:4341). Har sahifa nishonga besh xil yo'l bilan so'rov yubordi: fetch (credentials: "include"), forma POST, iframe, <img> va yuqori darajadagi havola. Natija (o'lchov jurnalidan):
--- boshqa sayt (127.0.0.1):
fetch-include → none
forma-post → none
havola-GET → __Host-sid,__Secure-s,httponly,lax,none,secure,sukut
iframe → none
rasm → none
--- bir sayt, boshqa port (localhost):
fetch-include → __Host-sid,__Secure-s,httponly,lax,none,qisqa,secure,strict,sukut
forma-post → __Host-sid,__Secure-s,httponly,lax,none,qisqa,secure,strict,sukut
havola-GET → __Host-sid,__Secure-s,httponly,lax,none,secure,strict,sukut
iframe → __Host-sid,__Secure-s,httponly,lax,none,qisqa,secure,strict,sukut
rasm → __Host-sid,__Secure-s,httponly,lax,none,qisqa,secure,strict,sukutBu yerda none — cookie nomi (SameSite=None bilan qo'yilgan), "hech narsa" emas. Ikki xulosa:
- Boshqa saytdan
fetch, forma,iframe, rasm — faqatnonecookie'si bordi.Laxva sukutdagilar faqat havola bosilganda qo'shildi;stricthatto havolada ham yo'q. SameSite aytilgandek ishladi. - Bir saytning boshqa portidan — hamma cookie,
strictham, hamma yo'l bilan bordi. Port sayt tushunchasiga kirmaydi.
Ikkinchi xulosa amaliy jihatdan juda muhim. Bitta kompyuterda yoki bitta domen ostida boshqa port yoki subdomenda ishlaydigan har qanday sahifa (masalan, buzilgan boshqa servis) uchun sizning SameSite=Strict cookie'ingiz himoya emas. Shuning uchun SameSite — CSRF'ga qarshi birinchi qatlam, yagonasi emas. Qo'shimcha qatlamlarni Tokenni qayerda saqlash va CSRF darsida quramiz.
flowchart TD
S[So'rov: cookie qo'shilsinmi?] --> D{Domain, Path mos, muddati o'tmagan?}
D -->|yo'q| N[Qo'shilmaydi]
D --> X{Secure bo'lsa: HTTPS yoki localhost?}
X -->|yo'q| N
X --> B{So'rovni boshlagan sahifa bir saytdami?}
B -->|ha| Y[Qo'shiladi]
B -->|yo'q| M{SameSite}
M -->|None| Y
M -->|Strict| N
M -->|Lax yoki yozilmagan| L{Yuqori darajadagi GET navigatsiyami?}
L -->|ha| Y
L -->|yo'q| NDiagramma kanon kuzatuvlarini umumlashtiradi. "Bir saytdami?" savoli portni hisobga olmaydi — yuqoridagi ikkinchi xulosa shu tugundan chiqadi.
Tekshirib ko'ring: Frontend
http://localhost:5173da (Vite), APIhttp://localhost:3000da. API cookie'siSameSite=Strict. Frontend'dagifetch(…, { credentials: "include" })cookie'ni yuboradimi?
Javob
Ha. Ikkalasi bitta sayt (localhost), port saytga kirmaydi — kanon o'lchovida "bir sayt, boshqa port" holatida strict ham yuborildi. Lekin origin'lar har xil, shuning uchun brauzer javobni JS'ga faqat CORS ruxsat bersa beradi (Access-Control-Allow-Credentials: true va aniq origin). Cookie va CORS — ikki alohida mexanizm.
5. Imzolangan cookie
Ba'zan cookie'da sir bo'lmagan, lekin o'zgartirilmasligi kerak bo'lgan qiymat saqlanadi: tanlangan til, "bannerni yopdi" belgisi. Foydalanuvchi uni brauzerda bemalol o'zgartira oladi. Server o'zgartirilganini bilishi uchun qiymatga imzo qo'shiladi — HMAC (crypto 1):
const enc = new TextEncoder();
const b64url = (buf) =>
btoa(String.fromCharCode(...new Uint8Array(buf)))
.replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
const baytga = (s) => Uint8Array.from(
atob(s.replaceAll("-", "+").replaceAll("_", "/")),
(c) => c.charCodeAt(0));
// faqat namoyish uchun — haqiqiy sir .env da, 32 bayt tasodifiy
const kalit = await crypto.subtle.importKey(
"raw", enc.encode("namuna-sir-faqat-dars-uchun-0001"),
{ name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"],
);
async function imzola(qiymat) {
const s = await crypto.subtle.sign(
"HMAC", kalit, enc.encode(qiymat));
return `${qiymat}.${b64url(s)}`;
}
async function tekshir(cookie) {
const i = cookie.lastIndexOf(".");
const [qiymat, imzo] = [cookie.slice(0, i), cookie.slice(i + 1)];
const togri = await crypto.subtle.verify(
"HMAC", kalit, baytga(imzo), enc.encode(qiymat));
return togri ? qiymat : null;
}
const c = await imzola("til=uz");
console.log(c);
console.log(await tekshir(c));
console.log(await tekshir(c.replace("til=uz", "til=ru")));Konsolda:
til=uz.DP1IG2i7EiKZhzd9AOhIgXtNbN-X9QWKAlP-kPVcje0
til=uz
nullQiymat ochiq ko'rinadi — imzo uni yashirmaydi, faqat o'zgarishini sezadi. til=ru ga almashtirilgan cookie null qaytardi: imzo endi mos emas. Tekshiruvda === emas, crypto.subtle.verify ishlatdik — u solishtirishni o'zi bajaradi va natijani faqat true/false bilan beradi.
Express olamida buni cookie-parser paketi qiladi (res.cookie(…, { signed: true }) → s: prefiksli qiymat). Keyingi darsda express-session ham sessiya ID'sini xuddi shunday imzolashini ko'rasiz. Lekin bitta muhim cheklov bor: imzo qiymatni sir qilmaydi. Cookie ichida parol, token yoki shaxsiy ma'lumot bo'lsa — imzo yordam bermaydi; u holda cookie'da faqat tasodifiy ID, ma'lumot esa serverda turishi kerak.
6. Uchinchi tomon cookie'lari va CHIPS
Birinchi tomon (first-party) cookie — siz ochgan saytning o'zi qo'ygan. Uchinchi tomon (third-party) — sahifaga joylashtirilgan boshqa sayt (reklama, vidjet, iframe) qo'ygan. Uchinchi tomon cookie'lari ko'p yillar foydalanuvchini saytdan saytga kuzatish uchun ishlatildi. Shuning uchun brauzerlar ularni cheklay boshladi (hujjatlar bo'yicha, 2026-10-28 holatiga):
| Brauzer | Uchinchi tomon cookie'lari |
|---|---|
| Safari (ITP) | sukut bo'yicha bloklangan |
| Firefox (Total Cookie Protection) | har sayt uchun alohida "quti"ga ajratilgan |
| Chrome | sukut bo'yicha ruxsat; Google 2024–2025 yillarda ularni o'chirish rejasidan voz kechdi va Privacy Sandbox loyihasining katta qismini to'xtatdi |
Kanon headless shell'da SameSite=None cookie'si boshqa saytdan yuborildi — Chromium'da bloklanmagan. Safari va Firefox sinalmagan.
Joylashtirilgan vidjetga (masalan, boshqa saytlarga qo'yiladigan chat oynasi) cookie kerak bo'lsa, zamonaviy yechim — CHIPS (Cookies Having Independent Partitioned State): Partitioned atributi. Bunday cookie har yuqori darajadagi sayt uchun alohida saqlanadi — vidjet a.uz va b.uz da ikki xil cookie oladi va foydalanuvchini saytlar orasida kuzata olmaydi. Kursdagi loyihada bunday ehtiyoj yo'q, kanon CHIPS'ni sinamagan.
vazifalar uchun xulosa oddiy: frontend va API bitta saytda (dev'da Vite proksi, prodakshnda bitta domen) — demak bizning sessiya cookie'miz birinchi tomon cookie'si va bu cheklovlar unga tegmaydi.
7. Hujumchi nigohi
Har hujum yonida himoya va uni tasdiqlagan o'lchov yoki test:
| Hujum | Himoya | Tekshiruv (bu darsda) |
|---|---|---|
XSS document.cookie ni o'qiydi |
HttpOnly |
document.cookie da __Host-sid yo'q |
| ochiq Wi-Fi'da ushlash | Secure + HTTPS |
Secure cookie faqat HTTPS (va localhost) |
| boshqa saytdan forma POST | SameSite=Lax/Strict |
boshqa saytdan faqat none bordi |
| bir saytning boshqa portidan | Origin tekshiruvi (CSRF darsi) | strict ham bordi — SameSite yetmaydi |
subdomen Domain= bilan qo'yadi |
__Host- prefiksi |
__Host-domen rad etildi |
| til cookie'sini almashtirish | imzo (HMAC) | til=ru → null |
| imzoga sir yashirish | cookie'da faqat ID | imzoli qiymat ochiq ko'rinadi |
Nega bu hujumlar ishlaydi — yuqorida ko'rdik: cookie o'zi qo'shiladi, SameSite portni ko'rmaydi, imzo shifrlamaydi.
8. Ko'p uchraydigan xatolar
| Xato | Nega xavfli | To'g'risi |
|---|---|---|
sessiya cookie'si HttpOnly siz |
bitta XSS — sessiya o'g'irlandi | HttpOnly |
SameSite=None "har ehtimolga qarshi" |
CSRF eshigi ochiq | Lax, kerak bo'lsa Strict |
Domain=example.com "qulaylik uchun" |
hamma subdomenga ketadi | Domain yozmang, __Host- |
Path ni xavfsizlik deb bilish |
bir host ichida chegara emas | alohida host yoki origin |
headers.get("set-cookie") bilan bo'lish |
Expires dagi vergul |
getSetCookie() |
| cookie'da JSON ko'rinishida profil | 4096 bayt, ochiq, o'zgartiriladi | serverda saqlash, cookie'da ID |
| "SameSite bor — CSRF yo'q" | boshqa port bir sayt | Origin tekshiruvi ham |
9. Mashqlar
Mashqlar kurs/mashqlar/28/06-cookie/ da.
1-mashq (oson): to'g'ri yoki noto'g'ri
HttpOnlycookie serverga yuborilmaydi, faqat brauzerda saqlanadi.localhost:5173valocalhost:3000— bitta sayt.SameSite=Strictcookie boshqa saytdagi havola bosilganda yuborilmaydi.- Chromium'da
Securecookiehttp://localhostda qabul qilinadi. - Imzolangan cookie'ning qiymatini foydalanuvchi o'qiy olmaydi.
Yechim
- Noto'g'ri —
HttpOnlyfaqat JS'dan yashiradi; cookie har so'rov bilan serverga boradi. - To'g'ri — port saytga kirmaydi; kanon o'lchovida "bir sayt, boshqa port" holatida hamma cookie bordi.
- To'g'ri — kanon: boshqa saytdan havola-GET'da
laxvasukutbordi,strictyo'q. - To'g'ri —
localhostva127.0.0.1ishonchli origin; kanon ikkalasida ham sinadi. - Noto'g'ri — imzo faqat o'zgarishni sezadi; qiymat ochiq (
til=uz.…).
2-mashq (o'rta): Set-Cookie yasovchi
setCookie(nom, qiymat, o) funksiyasini yozing. U nom=qiymat (qiymat — encodeURIComponent bilan), Path (sukut /), so'ng mavjud bo'lsa Max-Age, Domain, HttpOnly, Secure, SameSite ni ; bilan qo'shadi. Uch holatda xato tashlasin: nomda harf, raqam, _, - dan boshqa belgi bor; __Host- nomi Securesiz, Domain bilan yoki Path / emas; SameSite=None Securesiz. Ikkita to'g'ri va uchta xato misolda sinang. Ishora: xatoni ushlash — try/catch (try / catch / finally).
Yechim
function setCookie(nom, qiymat, o = {}) {
if (!/^[\w-]+$/.test(nom)) throw new Error(`nom yaroqsiz: ${nom}`);
const q = [`${nom}=${encodeURIComponent(qiymat)}`];
q.push(`Path=${o.path ?? "/"}`);
if (o.maxAge !== undefined) q.push(`Max-Age=${o.maxAge}`);
if (o.domain) q.push(`Domain=${o.domain}`);
if (o.httpOnly) q.push("HttpOnly");
if (o.secure) q.push("Secure");
if (o.sameSite) q.push(`SameSite=${o.sameSite}`);
const s = q.join("; ");
if (nom.startsWith("__Host-") && (!o.secure || o.domain ||
(o.path ?? "/") !== "/")) {
throw new Error("__Host-: Secure, Path=/ va Domain'siz bo'lsin");
}
if (o.sameSite === "None" && !o.secure) {
throw new Error("SameSite=None faqat Secure bilan");
}
return s;
}
console.log(setCookie("__Host-sessiya", "a b", {
httpOnly: true, secure: true, sameSite: "Lax", maxAge: 2592000,
}));
console.log(setCookie("til", "o'zbek"));
for (const [nom, o] of [
["__Host-x", { secure: true, domain: "example.com" }],
["t", { sameSite: "None" }],
["yomon nom", {}],
]) {
try {
setCookie(nom, "1", o);
} catch (x) {
console.log("xato:", x.message);
}
}Konsolda:
__Host-sessiya=a%20b; Path=/; Max-Age=2592000; HttpOnly; Secure; SameSite=Lax
til=o'zbek; Path=/
xato: __Host-: Secure, Path=/ va Domain'siz bo'lsin
xato: SameSite=None faqat Secure bilan
xato: nom yaroqsiz: yomon nomBo'shliq %20 ga aylandi — cookie qiymatida bo'shliq, ; va , bo'lmasligi kerak. o'zbek dagi apostrof encodeURIComponent da o'zgarmaydi — u cookie qiymatida ruxsat etilgan belgi. Brauzer rad etadigan cookie'ni serverning o'zi yaratmasligi yaxshiroq: xato jim yo'qolib ketmaydi.
3-mashq (qiyin): SameSite simulyatori
yuboriladimi(sameSite, s) funksiyasini yozing: s — { tur, metod, saytlararo }, tur — fetch, forma, havola yoki rasm. Bir saytdan bo'lsa — har doim true; boshqa saytdan: None — true, Strict — false, Lax — faqat havola va xavfsiz metod (GET, HEAD). Beshta so'rov uchun qaysi SameSite qiymatlari cookie'ni yuborishini chiqaring va natijani kanon o'lchovi bilan solishtiring. Ishora: filter va join (filter: shart bo'yicha tanlash).
Yechim
const XAVFSIZ = new Set(["GET", "HEAD"]);
function yuboriladimi(sameSite, s) {
if (!s.saytlararo) return true; // bir sayt — hamma rejim yuboradi
if (sameSite === "None") return true;
if (sameSite === "Strict") return false;
// Lax (va SameSite yozilmagan) — faqat yuqori darajadagi xavfsiz
// navigatsiyada
return s.tur === "havola" && XAVFSIZ.has(s.metod);
}
const SOROVLAR = [
{ tur: "fetch", metod: "GET", saytlararo: true },
{ tur: "forma", metod: "POST", saytlararo: true },
{ tur: "havola", metod: "GET", saytlararo: true },
{ tur: "rasm", metod: "GET", saytlararo: true },
{ tur: "forma", metod: "POST", saytlararo: false },
];
for (const s of SOROVLAR) {
const natija = ["Strict", "Lax", "None"]
.filter((ss) => yuboriladimi(ss, s)).join(",");
const joy = s.saytlararo ? "boshqa sayt" : "bir sayt";
console.log(`${s.tur} ${s.metod} (${joy}):`, natija);
}Konsolda:
fetch GET (boshqa sayt): None
forma POST (boshqa sayt): None
havola GET (boshqa sayt): Lax,None
rasm GET (boshqa sayt): None
forma POST (bir sayt): Strict,Lax,NoneNatija kanon o'lchovidagi jadval bilan mos: boshqa saytdan fetch, forma va rasmda faqat none, havolada lax va none ham, bir saytda — hammasi. Simulyator qoidani tushunish uchun; haqiqiy brauzerda yana nozik joylar bor (iframe ichidagi navigatsiya, yo'naltirishlar zanjiri), shuning uchun muhim qarorni doim haqiqiy brauzerda sinang — kanon Playwright bilan aynan shunday qildi.
10. Loyiha qadami: cookieOl() va COOKIE_SECURE
Bu darsning loyihadagi izi kichik, lekin keyingi dars uchun poydevor. vazifalar-api keyingi darsda sessiya cookie'sini qo'yadi va o'qiydi. O'qish uchun cookie-parser paketini qo'shish mumkin edi. Kanon boshqacha qaror qildi: bizga bitta cookie kerak, uni ajratib olish — o'n qatorli funksiya. Har yangi bog'liqlik — yangi xavf (Ta'minot zanjiri xavfsizligi).
Yangi fayl src/auth/sessiya.ts ning birinchi funksiyasi (keyingi darsda shu faylga sessiya ombori qo'shiladi):
/** Cookie sarlavhasidan bitta cookie (birinchisi). Express'da
* req.cookies faqat cookie-parser bilan — bitta nom uchun paket
* kerak emas */
export function cookieOl(
sarlavha: string | undefined,
nom: string,
): string | undefined {
if (sarlavha === undefined) return undefined;
for (const qism of sarlavha.split(";")) {
const i = qism.indexOf("=");
if (i !== -1 && qism.slice(0, i).trim() === nom) {
return qism.slice(i + 1).trim();
}
}
return undefined;
}Kanon kodini telefon uchun bo'ldik. Uni brauzerda ham sinab ko'ring (turlarsiz nusxa):
function cookieOl(sarlavha, nom) {
if (sarlavha === undefined) return undefined;
for (const qism of sarlavha.split(";")) {
const i = qism.indexOf("=");
if (i !== -1 && qism.slice(0, i).trim() === nom) {
return qism.slice(i + 1).trim();
}
}
return undefined;
}
const N = "__Host-sessiya";
console.log(cookieOl("a=1; __Host-sessiya=abc; b=2", N));
console.log(cookieOl("x__Host-sessiya=zz; __Host-sessiya=ok", N));
console.log(cookieOl("a=1", N));
console.log(cookieOl("__Host-sessiya=s=1=2", N));Konsolda:
abc
ok
undefined
s=1=2Ikkinchi qator — nozik joy: x__Host-sessiya ham "__Host-sessiya" bilan tugaydi. includes yoki endsWith bilan qidirgan sodda kod uning qiymatini (zz) qaytarardi. Kanon nomni = gacha kesib, aniq solishtiradi. To'rtinchi qator: qiymat ichidagi = saqlanadi — faqat birinchi = ajratuvchi.
Kanon testi (test/sessiya.test.ts ning birinchi testi, telefon uchun bo'lingan):
test("cookieOl — nomi aniq mos kelgan birinchi cookie", () => {
assert.equal(
cookieOl("a=1; __Host-sessiya=abc; b=2", "__Host-sessiya"),
"abc");
assert.equal(
cookieOl("x__Host-sessiya=zz; __Host-sessiya=ok",
"__Host-sessiya"),
"ok");
assert.equal(cookieOl("a=1", "__Host-sessiya"), undefined);
assert.equal(cookieOl(undefined, "a"), undefined);
});Biz uni kanon holat nusxasida alohida ishga tushirdik (Node 24.21.0):
node --test --test-name-pattern="cookieOl" test/sessiya.test.ts✔ cookieOl — nomi aniq mos kelgan birinchi cookie (149.1652ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 12282.919Kanon tajribasidan yana bitta qaror chiqdi — sozlama COOKIE_SECURE (sukut true). Secure va __Host- Chromium'da http://localhost da ham ishlagani uchun lokal dev'da ham prodakshndagi atributlar ishlatiladi. Faqat LAN IP yoki http domen orqali sinaganda false qilinadi; prodakshnda esa false taqiqlanadi. Bu sozlama keyingi darsda config.ts ga qo'shiladi.
Hali commit qilmang — sessiya.ts keyingi darsda sessiya ombori bilan to'ldiriladi va hammasi bitta commit bo'ladi.
11. Real ishda
- Brauzer DevTools → Application → Cookies: har cookie'ning atributlari, "SameSite" ustuni va rad etilgan cookie'lar sababi (Network panelida,
Set-Cookieyonida ogohlantirish belgisi) ko'rinadi. Atribut ishlamasa, birinchi qaraladigan joy. - Standart: cookie qoidalari RFC 6265 da. Uning yangi nashri (rfc6265bis — SameSite, prefikslar) hali RFC raqamini olmagan: IETF datatracker'iga ko'ra (2026-10-28 holatiga) oxirgi qoralama — 22-versiya (2025-12-01), hujjat RFC muharriri navbatida. Brauzerlar uni allaqachon amalda qo'llaydi.
- Sessiya cookie'si nomi:
__Host-prefiksli nomlar katta saytlarda keng tarqalgan. Nomning o'zi texnologiyani oshkor qilmasin (connect.sid— Express ekanini aytadi). - Intervyuda: "HttpOnly nimadan himoya qiladi va nimadan qilmaydi?", "SameSite=Lax va Strict farqi?", "Sayt va origin farqi?".
Xulosa
Set-Cookie— har cookie alohida qator; Node'dagetSetCookie().HttpOnly— JS'dan yashiradi,Secure— faqat HTTPS (Chromium'dalocalhostistisno),Path— xavfsizlik chegarasi emas,Domain— qo'ymaslik xavfsizroq.__Host-=Secure+Path=/+Domainsiz; aks holda brauzer rad etadi.SameSite=NonefaqatSecurebilan; 4096 baytdan kattasi saqlanmaydi.- SameSite saytga qaraydi (port va subdomen kirmaydi): boshqa saytdan faqat
None, havola-GET'daLaxham; bir saytning boshqa portidan — hammasi,Strictham. - Imzo o'zgarishni sezadi, lekin qiymatni yashirmaydi.
- Uchinchi tomon cookie'lari brauzerlarda cheklanmoqda;
vazifalarcookie'si birinchi tomon.
Keyingi dars: Session-based auth — __Host-sessiya cookie'si, Postgres'dagi sessiyalar jadvali, chiqish va "barcha qurilmalardan chiqish".
Manbalar
- RFC 6265 "HTTP State Management Mechanism": https://www.rfc-editor.org/rfc/rfc6265 va yangi nashr qoralamasi (rfc6265bis, draft-22, RFC raqami hali yo'q): https://datatracker.ietf.org/doc/draft-ietf-httpbis-rfc6265bis/ (2026-10-28 da tekshirildi)
- MDN — Set-Cookie (atributlar, prefikslar): https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie
- MDN — Cookies Having Independent Partitioned State (CHIPS): https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies
- web.dev — "Understanding same-site and same-origin": https://web.dev/articles/same-site-same-origin
- Google Privacy Sandbox yangiliklari (uchinchi tomon cookie'lari bo'yicha qarorlar): https://privacysandbox.com/news/
- WebKit — Full Third-Party Cookie Blocking (2020): https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ ; Mozilla — Total Cookie Protection (2022): https://blog.mozilla.org/en/mozilla/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/
- Kanon o'lchovi (IlmHamroh, 2026-10-27): cookie atributlari tajribasi (Playwright 1.64.0, Chromium headless shell 151.0.7922.34,
node:http). Brauzer va Node misollari,cookieOltesti — biz ishga tushirdik (2026-10-28, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!