Asosiy kontentga o‘tish

Maqolalar 11 daqiqa o‘qish 2 soat amaliyot 32 ko‘rish

XSS: foydalanuvchi kiritgan matn — matn emas, ehtimoliy kod

Brauzerga begona JavaScript qanday tushadi. Sabab bitta: kimdir kiritgan matn sahifaga qanday joylashtirilishiga qarab — matn bo'lib qoladimi yoki kodga aylanadimi. Beshta labda uch xil XSS'ni o'z qo'limiz bilan ishga tushiramiz, keyin uni chiqishda qochirish bilan yopamiz.

security backend

Mundarija

Oldingi darsda so'rovdagi ID'ni almashtirib birovning ma'lumotini o'qidik. Endi bir qadam nariga o'tamiz: ma'lumotni o'qish emas, sahifaga o'zimiznikini yozib qo'yish — va u boshqalarning brauzerida ishga tushsin.

Buning kaliti bitta savolda: siz kiritgan matn sahifaga qanday joylashtiriladi? Agar server uni oddiy matn deb emas, HTML deb joylashtirsa, sizning <script> ingiz ham HTML bo'lib — ya'ni kod bo'lib — tushadi. Bu XSS.

Bu darsdan keyin siz uch xil XSS'ni ajrata olasiz, ularni PortSwigger laboratoriyasida ishga tushira olasiz va nima uchun "matnni tozalash" emas, chiqishda qochirish to'g'ri yechim ekanini tushunasiz.

Bu dars kimga

1-dars (Burp) va 2-dars (IDOR) tugagan bo'lsin. HTML va biroz JavaScript bilan tanish bo'lsangiz osonroq ketadi.

Buzilgan taxmin#

Bu darsning taxmini deyarli har bir web-dasturchida bor:

"Foydalanuvchi kiritgan narsa — matn. Men uni shunchaki ko'rsataman."

Muammo "ko'rsataman" degan so'zda. Matnni sahifaga qo'yishning ikki yo'li bor va ular tubdan farq qiladi:

  • Matn sifatida — brauzer uni harflar deb ko'rsatadi. <b>salom</b> ekranda aynan shunday, teg bilan chiqadi.

  • HTML sifatida — brauzer uni ko'rsatma deb o'qiydi. <b>salom</b> qalin salom bo'lib chiqadi, <script> esa ishga tushadi.

Dasturchi matn kiritilishini kutadi, lekin uni HTML sifatida joylashtiradi — va shu yerda oddiy izoh maydoni begona kod kanaliga aylanadi. Foydalanuvchi kiritgan har qanday narsa — matn emas, ehtimoliy kod: matn bo'lib qoladimi yoki kodga aylanadimi, uni siz emas, joylashtirish usuli hal qiladi.

Atamalar#

Atama Ma'nosi
XSS Cross-Site Scripting — begona skriptni boshqa foydalanuvchi brauzerida ishga tushirish
reflected XSS payload so'rovda keladi, javobda darhol qaytadi (havola orqali hujum)
stored XSS payload serverda saqlanadi (izoh, profil) va har ochganda ishlaydi
DOM-based XSS server umuman aralashmaydi — brauzerdagi JS o'zi payloadni sahifaga qo'yadi
sink payload tushadigan xavfli joy (innerHTML, document.write)
source payload keladigan joy (location.search, input)
payload zaiflikni uyg'otuvchi qiymat, bu yerda <script>alert(1)</script>
context payload tushgan joyning turi — HTML, atribut ichi, JS satri ichi
escaping / encoding maxsus belgini zararsiz shaklga o'tkazish: < → &lt;
sanitization xavfli qismlarni olib tashlash (HTML tozalash)
CSP Content-Security-Policy — brauzerga qaysi skript ishlashi mumkinligini aytadi

Nazariya: uch xil XSS, bitta sabab#

XSS uch turga bo'linadi, lekin sabab bittada birlashadi — ishonchsiz ma'lumot bajariladigan joyga tozalanmasdan tushadi. Farqi — u qayerdan kelib qayerga tushishida.

Sxema
Reflected:  so'rov ──► server ──► javob (darhol qaytadi) ──► brauzer
Stored:     so'rov ──► server ──► BAZA ──► keyin har ochganda ──► brauzer
DOM-based:  so'rov ──────────────────────► brauzerdagi JS ──► sahifa
                                            (server ko'rmaydi ham)

Serverdagi zaif qolip (reflected/stored uchun):

Python
# Zaif: foydalanuvchi qidiruvi javobga to'g'ridan-to'g'ri qo'yiladi
def search(request):
    query = request.GET.get("q", "")
    html = f"<p>Natija: {query}</p>"  # <-- HTML sifatida
    return HttpResponse(html)

q ichida <script>alert(1)</script> bo'lsa, u <p> ichiga HTML bo'lib tushadi va ishga tushadi. DOM-based esa serverga umuman bormaydi — buni 5-labda ko'ramiz.

Kuzating

payloadni yuborgach, uning javobda qayerga tushganini toping (Burp'da javobni qidiring). Payload <p> ichidami, atribut ichidami, <script> ichidami — yechim aynan shunga bog'liq. Kontekst yechimni belgilaydi.

Amaliyot#

1-labHech narsa qochirilmagan#

Lab: Reflected XSS into HTML context with nothing encoded · APPRENTICE

Vazifa: alert funksiyasini ishga tushirish.

Nimani ko'rasiz: qidiruv maydoni. Yozganingiz javob sahifasida qaytadi.

Savol: qidiruvga <b>salom</b> yozsangiz — qalin "salom" chiqadimi, yoki teg bilan chiqadimi? Qaysi biri bo'lsa, XSS bor?

Eng oddiy tekshiruv: <script>alert(1)</script> ni qidiring.

Bu eng toza holat: server payloadni javobga hech o'zgartirmasdan qo'yadi. <b> qalin chiqsa — server sizning matningizni HTML deb o'qiyapti, demak <script> ham ishlaydi. Bu reflected XSS: payload so'rovda keldi, javobda darhol qaytdi. Haqiqiy hujumda buni qurbon bosishi uchun tayyor havolaga o'rab yuboriladi.

2-labServerda saqlanadi#

Lab: Stored XSS into HTML context with nothing encoded · APPRENTICE

Vazifa: blog izohiga alert ishga tushiruvchi kod qoldirish.

Nimani ko'rasiz: blog postiga izoh yozish formasi.

Savol: izohingiz saqlanadi va boshqa har bir odam postni ochganda ko'rinadi. Payload izohga tushsa, kim uni ishga tushiradi?

Eng oddiy tekshiruv: izoh maydoniga <script>alert(1)</script> yozib yuboring, keyin postni qayta oching.

Bu stored XSS — reflected'dan xavfliroq. Payload bazada yotadi va postni ochgan har bir foydalanuvchi brauzerida ishga tushadi. Hujumchi havola tarqatishi shart emas: qurbonlar o'zlari keladi. Bitta izoh — minglab brauzer.

3-labKontekstdan chiqib qochish#

Lab: Reflected XSS into attribute with angle brackets HTML-encoded · APPRENTICE

Vazifa: yana alert, lekin bu safar < va > qochirilgan.

Nimani ko'rasiz: payload HTML atributi ichiga tushadi, masalan value="siz yozgan narsa". Burchakli qavslar &lt; bo'lib qochirilgan, ya'ni <script> ishlamaydi.

Savol: < ishlamasa ham, siz allaqachon atribut ichidasiz. Atributdan chiqmasdan turib kod ishga tushira olasizmi?

Eng oddiy tekshiruv: "onmouseover="alert(1) — qo'shtirnoq bilan atributni yopib, yangi hodisa atributini qo'shing.

Mana bu — darsning eng muhim g'oyasi: kontekst yechimni belgilaydi. 1-labda <script> ishladi, chunki payload HTML ichida edi. Bu yerda < qochirilgan, lekin payload atribut ichida — demak qavs kerak emas, atributdan chiqish yetarli. Bir xil zaiflik, boshqa kontekst, boshqa payload. Payloadni yodlash foydasiz; kontekstni o'qishni bilish — foydali.

4-labJavaScript satri ichida#

Lab: Reflected XSS into a JavaScript string with angle brackets HTML encoded · APPRENTICE

Vazifa: yana alert, lekin payload endi <script> ichidagi JS satri ichiga tushadi.

Nimani ko'rasiz: payload var x = 'siz yozgan narsa'; kabi joyga tushadi. Qavslar qochirilgan, lekin siz allaqachon JS ichidasiz.

Savol: HTML tegini ocholmaysiz, lekin JS kodning ichidasiz — satrdan chiqib, o'z ifodangizni qo'sha olasizmi?

Eng oddiy tekshiruv: '-alert(1)-' — satrni yoping, alert(1) ni ifoda sifatida qo'shing, satrni qayta oching.

Uchinchi kontekst, uchinchi payload — lekin sabab o'sha: ma'lumot bajariladigan joyga tozalanmasdan tushdi. Bu yerda "bajariladigan joy" — JavaScript satri. Har kontekstda "chiqish"ning boshqa usuli bor, lekin qidiradigan narsa doim bitta: men hozir qayerdaman va bu yerdan qanday chiqib, kodga o'taman?

5-labServer umuman aralashmaydi#

Lab: DOM XSS in document.write sink using source location.search · APPRENTICE

Vazifa: alert — lekin bu safar payload serverga bormaydi.

Nimani ko'rasiz: sahifadagi JavaScript location.search (manzildagi so'rov qatori) dan qiymat oladi va uni document.write bilan sahifaga yozadi.

Savol: server payloadni ko'rmasa, uni kim sahifaga qo'yyapti? Zaiflik qayerda — serverdami yoki brauzerdami?

Eng oddiy tekshiruv: payloadni manzil qatoriga qo'ying va document.write uni sahifaga yozganini kuzating.

Bu DOM-based XSS va u boshqalardan tubdan farq qiladi: server payloadni ko'rmaydi ham. Manba (source) — location.search, xavfli joy (sink) — document.write. Ma'lumot brauzer ichida manbadan sink'ga tozalanmasdan oqib o'tadi. Shuning uchun uni faqat backend'da tuzatib bo'lmaydi — yechim frontend JS'da bo'lishi kerak.

Nima ishlamaydi va nega#

"Kirishda <script> so'zini bloklasam yetadi." Denylist doim teshik: <img src=x onerror=alert(1)>, <svg onload=...>, katta-kichik harf, kodlangan variantlar — <script> umuman kerak emas. Nimani taqiqlashni sanashga urinsangiz, doim bittasini unutasiz.

"Kirishni tozalab, xavfli belgilarni olib tashlayman." Kirishda tozalash noto'g'ri joy: bir xil matn HTML kontekstida xavfli, matn kontekstida zararsiz. Kirishda hali qaysi kontekstga tushishini bilmaysiz. 3 va 4-lab shuni ko'rsatdi — kontekst har xil.

"HTML-encode qildim, endi hammasi xavfsiz." 4-labda < va > qochirilgan edi, lekin XSS baribir ishladi — chunki payload JS satri ichida edi, u yerda burchakli qavs kerak ham emas. Bitta kontekst uchun to'g'ri qochirish boshqasida foyda bermaydi.

Endi yopamiz#

XSS'ning yechimi bitta jumlaga sig'adi: ma'lumotni kirishda emas, chiqishda — aynan tushadigan kontekstga qarab — qochiring.

Yaxshi xabar: zamonaviy shablon dvigatellari buni o'zi qiladi. Django'da {{ query }} avtomatik HTML-qochiradi:

Python
# To'g'ri: HttpResponse'ga qo'lda HTML yasamang — shablondan foydalaning
def search(request):
    query = request.GET.get("q", "")
    return render(request, "search.html", {"query": query})
Django/Jinja
{# search.html — {{ query }} avtomatik qochiriladi: <script> zararsiz matnga aylanadi #}
<p>Natija: {{ query }}</p>

<script>alert(1)</script> endi ekranda matn bo'lib chiqadi, chunki Django uni &lt;script&gt;... ga o'tkazadi. XSS yo'q.

Xavf |safe filtri va mark_safe() da — ular aynan qochirishni o'chiradi:

Python
# XAVFLI: mark_safe ishonchsiz ma'lumotga — qochirish o'chadi, XSS qaytadi
from django.utils.safestring import mark_safe

html = mark_safe(f"<p>{query}</p>")  # <-- buni qilmang

Muhim

|safe va mark_safe() ni faqat o'zingiz yozgan, ishonchli HTML'ga ishlating. Foydalanuvchidan kelgan narsaga hech qachon. Agar foydalanuvchiga HTML ruxsat berish kerak bo'lsa (izoh formatlash), bleach kabi kutubxona bilan allowlist asosida tozalang.

DOM-based (5-lab) boshqacha yopiladi. Server aralashmagani uchun Django yordam bera olmaydi. Yechim frontend'da: document.write va innerHTML o'rniga textContent ishlating, yoki qiymatni ishlatishdan oldin JS'da qochiring.

Ustiga: Content-Security-Policy — ikkinchi mudofaa qatori. Bu sayt allaqachon ishlatadi (nonce bilan), shuning uchun bironta payload teshib o'tsa ham, brauzer begona skriptni ishga tushirmaydi. CSP birlamchi himoya emas — qochirish birlamchi — lekin xato bo'lganda ushlab qoladi.

Bu saytning haqiqiy CSP sarlavhasi (soddalashtirilgan):

Natija
script-src 'self' 'nonce-<har javobda yangi>'; object-src 'none';
frame-ancestors 'none'; base-uri 'self'; default-src 'self'

Muhimi script-src 'self' 'nonce-...': faqat saytning o'z skriptlari va har javobda yangidan yasaladigan nonce ga ega inline skriptlar ishlaydi. Hujumchi joylagan <script> da to'g'ri nonce bo'lmaydi — brauzer uni ishga tushirmaydi. Django sozlamasi [config/settings/base.py] dagi CONTENT_SECURITY_POLICY da; django-csp middleware har javobga nonce qo'yadi.

Nazorat savollari#

  1. Matnni "matn sifatida" va "HTML sifatida" joylashtirish orasidagi farq nima? XSS qaysi biridan kelib chiqadi?
  2. Reflected, stored va DOM-based XSS orasidagi farqni ayting. Qaysi biri xavfliroq va nega?
  3. Nega XSS'ni kirishda emas, chiqishda qochirish kerak?
  4. 3-labda < qochirilgan bo'lsa ham XSS ishladi. Nega?
  5. "Kontekst yechimni belgilaydi" — buni 1 va 4-lab misolida tushuntiring.
  6. Django'da {{ query }} XSS'dan qanday himoya qiladi?
  7. |safe va mark_safe() nega xavfli? Ular qachon o'rinli?
  8. DOM-based XSS'ni nega faqat backend'da tuzatib bo'lmaydi?
  9. source va sink nima? 5-labda ular qaysi edi?
  10. CSP XSS'ni butunlay to'xtatadimi, yoki u qo'shimcha qatlammi? Sababini ayting.

Report uchun talablar#

  1. Skrinshot: beshta labdan kamida ikkitasi — alert(1) oynasi ko'ringan holatda.
  2. Kontekst: 3 va 4-labda payload qaysi kontekstga tushdi va nega ularda har xil payload kerak bo'ldi?
  3. Nima ishlamadi: biror payload ishlamadimi? Kontekstni noto'g'ri o'qidingizmi? Nega ishlamaganini yozing.
  4. Tuzatuvchi kod: o'z loyihangizdan (yoki tasavvuriy) foydalanuvchi kiritgan narsa HTML'ga tushadigan joy toping. Uni {{ }} shablon yoki qochirish bilan yoping; oldingi va keyingi holatni keltiring.

Faqat ruxsat berilgan joyda

alert(1) zararsiz ko'rinadi, lekin bir xil payload cookie o'g'irlashi yoki hisob egallashi mumkin. XSS'ni faqat PortSwigger laboratoriyasida yoki o'zingizga tegishli saytda sinang. Birovning saytiga skript joylashtirish — jinoyat.


Keyingi dars: CSRF — nega brauzer sizning nomingizdan, siz bilmagan holda so'rov yuborishi mumkin va uni token qanday to'xtatadi.

11 daqiqa 32 marta o‘qildi