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.
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: < → < |
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.
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):
# 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 < 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:
# 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})
{# 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 <script>... ga o'tkazadi. XSS yo'q.
Xavf |safe filtri va mark_safe() da — ular aynan qochirishni o'chiradi:
# 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):
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#
- Matnni "matn sifatida" va "HTML sifatida" joylashtirish orasidagi farq nima? XSS qaysi biridan kelib chiqadi?
- Reflected, stored va DOM-based XSS orasidagi farqni ayting. Qaysi biri xavfliroq va nega?
- Nega XSS'ni kirishda emas, chiqishda qochirish kerak?
- 3-labda
<qochirilgan bo'lsa ham XSS ishladi. Nega? - "Kontekst yechimni belgilaydi" — buni 1 va 4-lab misolida tushuntiring.
- Django'da
{{ query }}XSS'dan qanday himoya qiladi? |safevamark_safe()nega xavfli? Ular qachon o'rinli?- DOM-based XSS'ni nega faqat backend'da tuzatib bo'lmaydi?
sourcevasinknima? 5-labda ular qaysi edi?- CSP XSS'ni butunlay to'xtatadimi, yoki u qo'shimcha qatlammi? Sababini ayting.
Report uchun talablar#
- Skrinshot: beshta labdan kamida ikkitasi —
alert(1)oynasi ko'ringan holatda. - Kontekst: 3 va 4-labda payload qaysi kontekstga tushdi va nega ularda har xil payload kerak bo'ldi?
- Nima ishlamadi: biror payload ishlamadimi? Kontekstni noto'g'ri o'qidingizmi? Nega ishlamaganini yozing.
- 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.