14-bo‘lim

Cookie, sessiya va holat

HTTP nima uchun holatsiz, cookie qanday ishlaydi, sessiya ombori, cookie atributlari va imzolangan cookie.

🕑 8 daqiqa o‘qish 📄 756 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Cookie - eng sodda yechim
  2. Cookie atributlari
  3. Holatni qayerda saqlash kerak?
  4. Sessiya va token
  5. Xulosa

HTTP holatsiz (stateless) protokol: har so'rov mustaqil va server oldingisini eslamaydi.

Lekin saytga kirasiz, savatga mahsulot qo'shasiz, sahifani yangilaysiz - va hammasi joyida turadi. Bu qanday ishlaydi?

G'oya juda oddiy: server javobda kichik matn beradi, brauzer uni saqlaydi va har keyingi so'rovda qaytaradi.

Cookie qanday yuradi Brauzer Server GET / (cookie yo'q) Set-Cookie: sessiya=s-abc123 brauzer uni saqlab qo'yadi GET /savat Cookie: sessiya=s-abc123 "Xush kelibsiz, Husanboy" Server holatni cookie da emas, o'zida saqlaydi - cookie faqat KALIT
Cookie - bu shkafdagi raqamli jeton, shkafning o'zi emas
Python
import socket
import threading

PORT = 9800

tinglovchi = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tinglovchi.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tinglovchi.bind(("127.0.0.1", PORT))
tinglovchi.listen(4)

# Server tomonidagi sessiya ombori
SESSIYALAR = {"s-abc123": {"ism": "Husanboy", "korishlar": 0}}


def sarlavha_top(sorov, nom):
    for q in sorov.split("\r\n")[1:]:
        if q.lower().startswith(nom.lower() + ":"):
            return q.split(":", 1)[1].strip()
    return None


def server(nechta):
    for _ in range(nechta):
        ulanish, _ = tinglovchi.accept()
        sorov = ulanish.recv(4096).decode()
        cookie = sarlavha_top(sorov, "Cookie")

        qoshimcha = ""
        if cookie and "sessiya=" in cookie:
            sid = cookie.split("sessiya=")[1].split(";")[0]
            sessiya = SESSIYALAR.get(sid)
            if sessiya:
                sessiya["korishlar"] += 1
                tana = (f"Xush kelibsiz, {sessiya['ism']}! "
                        f"Ko'rishlar: {sessiya['korishlar']}")
            else:
                tana = "Sessiya topilmadi"
        else:
            tana = "Salom, mehmon! Sizga sessiya berildi."
            qoshimcha = ("Set-Cookie: sessiya=s-abc123; Path=/; "
                         "HttpOnly; SameSite=Lax\r\n")

        yuk = tana.encode()
        javob = (f"HTTP/1.1 200 OK\r\n{qoshimcha}"
                 f"Content-Length: {len(yuk)}\r\n"
                 f"Connection: close\r\n\r\n").encode() + yuk
        ulanish.sendall(javob)
        ulanish.close()


oqim = threading.Thread(target=server, args=(3,), daemon=True)
oqim.start()


def sorov_yubor(cookie=None):
    m = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    m.settimeout(5)
    m.connect(("127.0.0.1", PORT))
    satr = "GET / HTTP/1.1\r\nHost: x\r\n"
    if cookie:
        satr += f"Cookie: {cookie}\r\n"
    m.sendall((satr + "\r\n").encode())
    javob = b""
    while True:
        b = m.recv(1024)
        if not b:
            break
        javob += b
    m.close()
    sarlavhalar, tana = javob.decode().split("\r\n\r\n", 1)
    return sarlavhalar, tana


print("1-tashrif (cookie yo'q):")
s, t = sorov_yubor()
for q in s.split("\r\n"):
    if q.startswith("Set-Cookie"):
        print("  server berdi ->", q)
print("  tana:", t)

print()
print("2-tashrif (brauzer cookie ni qaytaradi):")
s, t = sorov_yubor("sessiya=s-abc123")
print("  tana:", t)

print()
print("3-tashrif:")
s, t = sorov_yubor("sessiya=s-abc123")
print("  tana:", t)

oqim.join()
tinglovchi.close()
Natija
1-tashrif (cookie yo'q):
  server berdi -> Set-Cookie: sessiya=s-abc123; Path=/; HttpOnly; SameSite=Lax
  tana: Salom, mehmon! Sizga sessiya berildi.

2-tashrif (brauzer cookie ni qaytaradi):
  tana: Xush kelibsiz, Husanboy! Ko'rishlar: 1

3-tashrif:
  tana: Xush kelibsiz, Husanboy! Ko'rishlar: 2

Protokol hali ham holatsiz - har so'rov mustaqil. Holat esa serverdagi lug'atda va cookie unga kalit bo'lib xizmat qiladi.

Sessiya identifikatori tasodifiy bo'lishi shart

Misolda s-abc123 deb qo'ydik, lekin haqiqiy dasturda u taxmin qilib bo'lmaydigan bo'lishi kerak.

1, 2, 3 kabi ketma-ket raqamlar berilsa, kimdir boshqa raqamni yozib qo'yib, o'zga odamning sessiyasiga kirib oladi.

Python da to'g'ri yo'l:

Python
import secrets
sid = secrets.token_urlsafe(32)

random modulini ishlatmang - u xavfsizlik uchun mo'ljallanmagan.

Cookie qiymatidan tashqari bir nechta atribut ham uzatiladi. Ular cookie qachon va qayerga yuborilishini belgilaydi:

Python
COOKIELAR = [
    "sessiya=s-abc123; Path=/; HttpOnly; Secure; SameSite=Strict",
    "mavzu=qorong; Path=/; Max-Age=31536000",
    "kuzatuv=xyz; Domain=.reklama.example; SameSite=None; Secure",
]

IZOHLAR = {
    "httponly": "JavaScript o'qiy olmaydi",
    "secure": "faqat HTTPS orqali",
    "samesite": "begona saytdan kelganda cheklanadi",
    "max-age": "necha soniya saqlanadi",
    "path": "qaysi yo'llarda yuboriladi",
    "domain": "qaysi domenlarga yuboriladi",
}

for matn in COOKIELAR:
    bolaklar = [b.strip() for b in matn.split(";")]
    nom, qiymat = bolaklar[0].split("=", 1)
    print(f"{nom} = {qiymat}")
    for b in bolaklar[1:]:
        kalit = b.split("=")[0]
        qiy = b.split("=", 1)[1] if "=" in b else "-"
        print(f"   {kalit:<9} {qiy:<10} {IZOHLAR.get(kalit.lower(), '')}")
    print()

print("xavfsizlik uchun eng muhim uchtasi:")
for k in ["httponly", "secure", "samesite"]:
    print(f"  {k:<9} {IZOHLAR[k]}")
Natija
sessiya = s-abc123
   Path      /          qaysi yo'llarda yuboriladi
   HttpOnly  -          JavaScript o'qiy olmaydi
   Secure    -          faqat HTTPS orqali
   SameSite  Strict     begona saytdan kelganda cheklanadi

mavzu = qorong
   Path      /          qaysi yo'llarda yuboriladi
   Max-Age   31536000   necha soniya saqlanadi

kuzatuv = xyz
   Domain    .reklama.example qaysi domenlarga yuboriladi
   SameSite  None       begona saytdan kelganda cheklanadi
   Secure    -          faqat HTTPS orqali

xavfsizlik uchun eng muhim uchtasi:
  httponly  JavaScript o'qiy olmaydi
  secure    faqat HTTPS orqali
  samesite  begona saytdan kelganda cheklanadi
Sessiya cookie si uchun uchta atribut majburiy
AtributNimadan himoya qiladi
HttpOnlySahifaga tushib qolgan skript cookie ni o'qiy olmaydi
SecureShifrlanmagan HTTP orqali yuborilmaydi
SameSiteBoshqa saytdagi forma sizning nomingizdan so'rov yubora olmaydi

Uchalasini ham qo'yish - bir qator kod, lekin ular eng ko'p uchraydigan uchta hujum turini birdan qiyinlashtiradi.

SameSite uchun qiymat tanlash:

QiymatQachon
StrictBank, admin panel - eng qattiq
LaxOdatdagi saytlar uchun yaxshi standart
NoneFaqat haqiqatan kerak bo'lsa, Secure bilan birga

Bu mavzular "Kiberxavfsizlik asoslari" darsligining 9-bo'limida batafsil ko'rilgan.

Holatni qayerda saqlash kerak? #

Ikki yondashuv bor va ikkalasi ham keng ishlatiladi:

Server tomondaCookie ichida
Cookie da nima borFaqat identifikatorMa'lumotning o'zi
HajmiKichik4 KB gacha
Bekor qilishOson - omborda o'chirasizQiyin
Serverlar ko'p bo'lsaUmumiy ombor kerakKerak emas
O'zgartirib bo'ladimiYo'qImzo bo'lmasa - ha

Oxirgi qator hal qiluvchi. Ma'lumotni cookie ichida saqlasangiz, uni imzolash shart:

Python
import hashlib
import hmac

MAXFIY = b"server-kaliti-hech-kimga-aytilmaydi"


def imzola(malumot):
    imzo = hmac.new(MAXFIY, malumot.encode(), hashlib.sha256).hexdigest()
    return f"{malumot}.{imzo[:16]}"


def tekshir(cookie):
    if "." not in cookie:
        return None
    malumot, imzo = cookie.rsplit(".", 1)
    kutilgan = hmac.new(MAXFIY, malumot.encode(),
                        hashlib.sha256).hexdigest()[:16]
    # hmac.compare_digest - vaqt bo'yicha xavfsiz taqqoslash
    if hmac.compare_digest(imzo, kutilgan):
        return malumot
    return None


toza = imzola("foydalanuvchi=husanboy&rol=oquvchi")
print("server bergan cookie:")
print("  ", toza)

print()
print("tekshiruv:", tekshir(toza))

# Endi kimdir qiymatni o'zgartirmoqchi bo'ladi
buzilgan = toza.replace("rol=oquvchi", "rol=admin")
print()
print("o'zgartirilgan cookie:")
print("  ", buzilgan)
print("tekshiruv:", tekshir(buzilgan))

print()
print("imzo mos kelmadi, chunki maxfiy kalit faqat serverda")
print("qiymat KO'RINADI, lekin uni O'ZGARTIRIB bo'lmaydi")
Natija
server bergan cookie:
   foydalanuvchi=husanboy&rol=oquvchi.efb38b66f8dd0c84

tekshiruv: foydalanuvchi=husanboy&rol=oquvchi

o'zgartirilgan cookie:
   foydalanuvchi=husanboy&rol=admin.efb38b66f8dd0c84
tekshiruv: None

imzo mos kelmadi, chunki maxfiy kalit faqat serverda
qiymat KO'RINADI, lekin uni O'ZGARTIRIB bo'lmaydi
Imzolash - shifrlash emas

Yuqoridagi cookie da rol=oquvchi ochiq ko'rinadi. Imzo uni yashirmaydi - u faqat o'zgartirishdan himoya qiladi.

Shuning uchun cookie ichiga hech qachon quyidagilarni yozmang:

YozmangSabab
Parol yoki uning xeshiOchiq ko'rinadi
Shaxsiy ma'lumotBrauzer diskida saqlanadi
Ichki ID larTizim tuzilishini oshkor qiladi

Maxfiy narsa kerak bo'lsa - uni serverda saqlang va cookie da faqat identifikator bering.

hmac.compare_digest ga ham e'tibor bering: oddiy == taqqoslash qancha belgi mos kelganiga qarab turlicha vaqt oladi va bu ma'lumot sizib chiqishiga yo'l ochadi.

Sessiya va token #

Zamonaviy loyihalarda yana bir yondashuv keng tarqalgan - token (masalan JWT). U aslida "cookie ichida ma'lumot" usulining rivojlangan shakli:

SessiyaToken
Holat qayerdaServerdaTokenning o'zida
Bekor qilishDarholMuddati tugagunicha kutiladi
Bir necha serverUmumiy ombor kerakKerak emas
HajmiBir necha baytBir necha yuz bayt

Bekor qilish masalasi ko'pincha kam baholanadi: foydalanuvchi "barcha qurilmalardan chiqish" tugmasini bosganda token yondashuvida uni darhol to'xtatib bo'lmaydi.

Shuning uchun amalda ko'pincha ikkalasi birga ishlatiladi: qisqa muddatli token va uni yangilaydigan, serverda saqlanadigan yozuv.

Amaliy topshiriq
  1. Cookie misolini ishga tushiring.
  2. Serverga ikkinchi sessiya qo'shing va uni sinang.
  3. Mavjud bo'lmagan sessiya ID si bilan so'rov yuboring.
  4. secrets.token_urlsafe bilan tasodifiy ID yasang.
  5. Atributlar misolini ishga tushiring va yangi cookie qo'shing.
  6. Brauzer ishlab chiquvchi panelida haqiqiy saytning cookie larini ko'ring.
  7. Imzolangan cookie misolini ishga tushiring.
  8. MAXFIY kalitni o'zgartiring - eski cookie hali ishlaydimi?
  9. Imzoni 16 belgidan 8 ga qisqartiring va bu nima uchun yomon ekanini yozing.
  10. O'z loyihangiz uchun sessiya yoki token tanlang va sababini asoslang.

Xulosa #

  • HTTP holatsiz: har so'rov mustaqil, server oldingisini eslamaydi.
  • Cookie - server beradigan va brauzer qaytaradigan kichik matn.
  • Server odatda cookie da faqat identifikator saqlaydi, holat esa o'zida turadi.
  • Sessiya identifikatori taxmin qilib bo'lmaydigan bo'lishi shart (secrets).
  • HttpOnly, Secure va SameSite - sessiya cookie si uchun majburiy uchlik.
  • SameSite=Lax - ko'p sayt uchun to'g'ri standart tanlov.
  • Ma'lumotni cookie ichida saqlasangiz, uni imzolash shart.
  • Imzolash shifrlash emas: qiymat ochiq ko'rinadi, faqat o'zgartirib bo'lmaydi.
  • Imzoni hmac.compare_digest bilan taqqoslang, == bilan emas.
  • Token yondashuvining asosiy narxi - uni darhol bekor qilib bo'lmasligi.

Keyingi bo'limda barcha bu ma'lumotlarni yo'lda himoya qiladigan qatlamni - TLS ni ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.