Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. XSS'ning uch turi
- 2.1 Begona kod qayerdan keladi?
- 2.2 Source va sink
- 2.3 Jonli: havoladan kelgan XSS
- 3. Cookie va HttpOnly: nima qutqaradi, nima yo'q
- 4. URL sink'lari: javascript: havolalar
- 4.1 Ekranlash yetmaydigan joy
- 4.2 Tekshiruv: faqat ruxsat etilgan protokollar
- 4.3 target="_blank" va window.opener
- 5. HTML kerak bo'lsa: sanitizatsiya
- 5.1 DOMPurify
- 5.2 setHTML: brauzerning o'z tozalovchisi
- 6. Brauzer darajasidagi himoya: CSP va Trusted Types
- 6.1 CSP: inline skriptni to'xtatish
- 6.2 Trusted Types: sink'larni qulflash
- Hujumchi nigohi
- 7. Ko'p uchraydigan xatolar
- 7.1 Xavfli so'zlarni o'chirish
- 7.2 Noto'g'ri joyda ekranlash
- 7.3 "Bu ma'lumot o'zimizning serverdan"
- 7.4 Tozalangan HTML'ni keyin o'zgartirish
- 7.5 Tozalash uchun innerHTML = ""
- 8. Mashqlar
- 1-mashq (oson): Sink'larni toping
- 2-mashq (o'rta): Xavfsiz profil havolasi
- 3-mashq (qiyin): DOMPurify + Trusted Types
- 4-mashq: Vazifalar qadami — XSS auditi
- 9. Real ishda
- Xulosa
- Manbalar
XSS va DOM xavfsizligi: source, sink, sanitizatsiya va Trusted Types
Qisqacha: XSS — begona matn sahifangizda kod bo'lib ishlashi. U uch yo'l bilan keladi: serverda saqlangan (stored), havoladan qaytgan (reflected) va brauzerning o'zida — JavaScript begona qiymatni xavfli joyga yozganda (DOM XSS). Himoya qatlamlari: matnni faqat
textContentbilan qo'yish, havoladahttps:ni tekshirish, HTML kerak bo'lsa DOMPurify yokisetHTMLbilan tozalash va brauzer darajasida CSP hamda Trusted Types bilan xavfli joylarni yopish.
Bu darsda
- Stored, reflected va DOM XSS farqini va har birining yo'lini tushuntira olasiz.
- Kodda source (begona qiymat manbai) va sink (uni kodga aylantiradigan joy) larni topa olasiz.
javascript:havolalardan URL tekshiruvi bilan himoyalanasiz.- HTML'ni DOMPurify va
setHTMLbilan tozalaysiz, Trusted Types bilan xavfli joylarni brauzer darajasida yopasiz. vazifalarda XSS auditini o'tkazib, himoyani ESLint qoidalari bilan qotirasiz.
Oldin bilishingiz kerak: Kontentni o'zgartirish va XSS xavfi, HTML darajasidagi xavfsizlik, Tagged template literals, eval, new Function va with, URL va URLSearchParams.
1. Nega bu kerak?
Kontentni o'zgartirish va XSS xavfi darsida birinchi hujumni ko'rdik: izohdagi <img onerror> innerHTML orqali kodga aylandi. Tagged template literals darsida html tegi bilan ekranlashni, eval darsida esa satrdan kod yasash xavfini ko'rdik. Bu dars ularni bitta tizimga yig'adi.
XSS (Cross-Site Scripting) nima uchun frontenddagi eng xavfli xato? Chunki hujumchining kodi sizning saytingiz nomidan ishlaydi. Brauzer uni "o'ziniki" deb biladi va unga hamma narsani beradi: sahifadagi ma'lumot, localStorage dagi token, HttpOnly bo'lmagan cookie'lar, mehmon nomidan so'rov yuborish, soxta "Qayta kiring" oynasi.
2005 yilda MySpace ijtimoiy tarmog'ida Samy Kamkar profiliga kichik XSS kodi qo'ydi. Profilni ochgan har kim uni "do'st" qilib qo'shar va kodni o'z profiliga ko'chirardi. 20 soatda bir milliondan ortiq profil zararlandi. Bunday o'zini o'zi ko'paytirib tarqaladigan zararli kod qurt (worm) deyiladi — Samy qurti tarixdagi eng tez tarqalganlaridan biri. Hammasi bitta tozalanmagan maydondan boshlandi.
2. XSS'ning uch turi
2.1 Begona kod qayerdan keladi?
Uch turning farqi — zararli matn qayerda "yashaydi" va kim uni sahifaga qo'yadi:
| Tur | Matn qayerda | Kim sahifaga qo'yadi |
|---|---|---|
| Stored (saqlangan) | Serverdagi bazada (izoh, ism) | Server yoki JS — har bir mehmonga |
| Reflected (qaytgan) | Havolada (?q=...) |
Server javobga qo'shib qaytaradi |
| DOM XSS | Havola, postMessage, xotira |
Brauzerdagi JavaScript |
Stored eng xavflisi: bir marta yozilgan izoh minglab mehmonga zarar beradi. Reflected uchun hujumchi qurbonni havolani bosishga undashi kerak — Telegram guruhga "chegirma" havolasi tashlaydi. DOM XSS esa serverga umuman tegmasligi mumkin: masalan, # dan keyingi qism serverga yuborilmaydi, server uni ko'rmaydi va tekshira olmaydi.
flowchart TD
H["Hujumchi matni"] --> S["Server bazasi<br/>(stored)"]
H --> U["Havola ?q=...<br/>(reflected, DOM)"]
S --> J["Sahifadagi JavaScript"]
U --> J
J -->|"innerHTML, href,<br/>eval"| X["Kod ishladi"]
J -->|"textContent,<br/>tekshirilgan URL"| T["Oddiy matn"]
classDef xavf fill:#fdeee6,stroke:#b4461a
classDef xavfsiz fill:#e8f1ec,stroke:#1f6f4a
class X xavf
class T xavfsizNimaga qarang: qayerdan kelmasin, oxirgi qaror bitta joyda — JavaScript qiymatni qayerga yozadi.
2.2 Source va sink
Xavfsizlik mutaxassislari buni ikki so'z bilan ifodalaydi:
- Source (manba) — begona qiymat kodga kiradigan joy:
location.search,location.hash,document.referrer,postMessagexabari (Oyna va tablar orasida aloqa), forma maydoni, serverdan kelgan JSON, hattolocalStorage(uni oldin kimdir yozgan bo'lishi mumkin). - Sink — qiymatni HTML yoki kod sifatida bajaradigan joy. XSS xavfi darsida birinchi uchtasini ko'rgandik, to'liq ro'yxat:
| Sink turi | Misollar |
|---|---|
| HTML | innerHTML, outerHTML, insertAdjacentHTML, document.write, iframe.srcdoc |
| JavaScript | eval, new Function, setTimeout("satr"), script.textContent, script.src |
| URL | a.href, iframe.src, location.href, form.action — javascript: bilan |
DOM XSS — source'dan kelgan qiymat tekshirilmasdan sink'ka tushishi. Audit qilish oson: kodda sink'larni qidirasiz va har biri uchun "bu qiymat qayerdan keldi?" deb so'raysiz.
2.3 Jonli: havoladan kelgan XSS
Sardor qidiruv natijasini ko'rsatadigan kichik sahifa yozdi: havoladagi ?q= qiymatini oladi va "Qidiruv: osh" deb chiqaradi. Natija oynasi o'z manziliga ega emas, shuning uchun havolani maydondan olamiz:
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
input { font: inherit; width: 100%; box-sizing: border-box; }
button { font: inherit; padding: 0.4rem 0.8rem; margin: 0.5rem 0; }
</style>
<label for="link">Do'stingiz yuborgan havola:</label>
<input id="link" value="https://example.com/qidiruv?q=osh">
<button id="unsafe" type="button">innerHTML bilan</button>
<button id="safe" type="button">textContent bilan</button>
<p id="result"></p>
<script>
document.cookie = "sessiya=abc123"; // namunaviy sessiya
const result = document.getElementById("result");
function readQuery() {
const url = new URL(document.getElementById("link").value);
return url.searchParams.get("q"); // source
}
document.getElementById("unsafe").addEventListener("click", () => {
result.innerHTML = `Qidiruv: <b>${readQuery()}</b>`; // sink
});
document.getElementById("safe").addEventListener("click", () => {
const bold = document.createElement("b");
bold.textContent = readQuery();
result.replaceChildren("Qidiruv: ", bold);
});
</script>Avval ikkala tugmani bosing — ikkalasi ham "Qidiruv: osh". Endi maydonni tozalab, hujumchi havolasini qo'ying:
https://example.com/qidiruv?q=%3Cimg%20src%3Dx%20onerror%3D%22console.log(document.cookie)%22%3E%3C — < ning URL'dagi ko'rinishi (URL va URLSearchParams darsidagi kodlash). searchParams.get uni ochadi va <img src=x onerror="..."> chiqadi. «innerHTML bilan» ni bosing — konsolga sessiya=abc123 chiqadi: hujumchining kodi cookie'ni o'qidi. Haqiqiy hujumda console.log o'rnida uni hujumchi serveriga yuborish turardi. «textContent bilan» — konsol jim, ekranda esa teg harflar bo'lib turadi.
Biz buni Chrome 154 da sinadik: xavfli tugmada konsolda aynan sessiya=abc123, xavfsizida — hech narsa.
Tekshirib ko'ring: Server
?q=ni umuman o'qimaydi, sahifa statik (GitHub Pages). Bu sahifa XSS'dan xavfsizmi?
Javob
Yo'q. Bu aynan DOM XSS: zararli qiymat havoladan to'g'ridan-to'g'ri brauzerdagi JavaScript'ga keladi va innerHTML ga tushadi. Server ishtirok etmaydi. Statik sayt ham XSS'ga ochiq — agar uning JavaScript'i source'ni sink'ka yozsa.
3. Cookie va HttpOnly: nima qutqaradi, nima yo'q
Yuqoridagi hujum document.cookie ni o'qidi. Cookie'lar JS'dan darsida ko'rgandik: server cookie'ga HttpOnly bayrog'ini qo'ysa, JavaScript uni umuman ko'rmaydi. Sessiya cookie'si HttpOnly bo'lsa, hujumchi uni o'g'irlay olmaydi.
Lekin bu XSS'ni zararsiz qilmaydi. Hujumchining kodi sahifa ichida ishlayapti — u cookie'ni olib ketmasa ham, fetch bilan mehmon nomidan so'rov yubora oladi: brauzer cookie'ni o'zi qo'shadi. Buyurtmani bekor qilish, parolni o'zgartirish, xabar yuborish — hammasi mumkin, faqat mehmon sahifani ochiq turgan paytda.
Shuning uchun HttpOnly — zarar hajmini kamaytiradi, XSS'ni to'xtatmaydi. XSS'ni faqat bitta narsa to'xtatadi: begona matn sink'ka tushmasligi.
4. URL sink'lari: javascript: havolalar
4.1 Ekranlash yetmaydigan joy
Mehmon profilida "Mening Telegramim" havolasi bor. Sardor uni shunday chiqardi:
// ❌ havola manzili tekshirilmagan
link.href = profile.website; // source: mehmon yozgan matn
link.textContent = "Mening sahifam";textContent — xavfsiz. Lekin href ga mehmon javascript:fetch('https://...') yozsa-chi? Havola bosilganda brauzer manzil o'rniga kodni bajaradi. Biz Live Server'dagi kabi oddiy sahifada sinadik: href="javascript:console.log(...)" havolasi bosilganda Chrome 154 kodni bajardi. Bu yerda ekranlash yordam bermaydi: javascript:alert(1) ichida birorta ham < yoki " yo'q.
4.2 Tekshiruv: faqat ruxsat etilgan protokollar
Qoida — oq ro'yxat: faqat https: va http: ga ruxsat, qolgani rad:
function isSafeUrl(input) {
try {
const url = new URL(input, "https://example.com/");
return url.protocol === "https:" || url.protocol === "http:";
} catch {
return false; // URL umuman o'qilmadi
}
}
console.log(isSafeUrl("https://t.me/sizning_kanal")); // true
console.log(isSafeUrl("/menyu")); // true
console.log(isSafeUrl("javascript:alert(1)")); // false
console.log(isSafeUrl(" JavaScript:alert(1)")); // false
console.log(isSafeUrl("java\tscript:alert(1)")); // false
console.log(isSafeUrl("data:text/html,<b>salom</b>")); // falseNega startsWith("javascript:") emas? Hujumchi katta harf, boshida bo'sh joy yoki o'rtada tab yozadi — brauzer baribir ularni javascript: deb tushunadi. new URL esa brauzer bilan bir xil qoidalar bo'yicha o'qiydi: uchala hiyla ham protocol === "javascript:" beradi va rad etiladi. Ikkinchi argument ("https://example.com/") — nisbiy manzillar (/menyu) uchun asos. URL darsida filtr uchun oq ro'yxat qilgan edik — g'oya o'sha.
4.3 target="_blank" va window.opener
Yana bir eski xavf. Havola yangi tabda ochilsa, yangi sahifa window.opener orqali sizning tabingizga yeta olardi va uni, masalan, soxta login sahifasiga almashtirardi. Bu reverse tabnabbing (teskari tab o'g'irlash) deyiladi. Himoya — rel="noopener" (Maxsus havolalar).
Yaxshi xabar: zamonaviy brauzerlarda target="_blank" o'zi noopener ni bildiradi. Biz Chrome 154 da tekshirdik: oddiy target="_blank" havolasida yangi tabdagi window.opener — null. rel="opener" qo'shilgandagina u bor. JavaScript'dan ochganda esa o'zingiz ayting: window.open(url, "_blank", "noopener").
Tekshirib ko'ring: Sardor himoya yozdi:
if (url.startsWith("javascript:")) return;. Mehmon havola maydonigaJavaScript:alert(1)(boshida bo'sh joy, katta J bilan) yozdi. Himoya ishlaydimi?
Javob
Yo'q. startsWith aynan shu harflarni qidiradi: bo'sh joy va katta J uni chalg'itadi. Brauzer esa bo'sh joyni tashlab, katta-kichik harfga qaramay, buni baribir javascript: deb tushunadi — kod ishlaydi. Shuning uchun qora ro'yxat ("buni taqiqla") emas, oq ro'yxat ("faqat https: va http: ga ruxsat") va new URL bilan tekshiriladi.
5. HTML kerak bo'lsa: sanitizatsiya
5.1 DOMPurify
Ba'zan HTML'ga ruxsat berish kerak: izohlarda qalin va kursiv, maqola muharriri. Bunda matnni sanitizatsiya (tozalash) qilinadi: xavfsiz teglar qoladi, qolgani olib tashlanadi. Tozalovchini o'zingiz yozmang (HTML darajasidagi xavfsizlik). Brauzer HTML'ni qanday o'qishini (Brauzer HTML'ni qanday tahlil qiladi) to'liq hisobga olish juda qiyin.
Eng ko'p ishlatiladigan kutubxona — DOMPurify (biz 3.4.16 bilan sinadik, 2026-oktabr). Natija oynasida uni jsDelivr'dan ulaymiz:
<div id="comment"></div>
<script type="module">
import DOMPurify
from "https://cdn.jsdelivr.net/npm/dompurify@3.4.16/+esm";
const dirty = '<b>Osh</b> zo\'r! '
+ '<img src="x" onerror="console.log(1)">'
+ '<a href="javascript:alert(1)">bos</a>';
console.log(DOMPurify.sanitize(dirty));
console.log(DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ["b", "i"],
ALLOWED_ATTR: [],
}));
</script>Konsolda:
<b>Osh</b> zo'r! <img src="x"><a>bos</a>
<b>Osh</b> zo'r! bosSukut bo'yicha DOMPurify xavfsiz teglarni qoldiradi, lekin xavfli qismlarni olib tashlaydi: onerror atributi va javascript: manzil yo'q. Ikkinchi chaqiruvda biz faqat b va i ga ruxsat berdik — rasm va havola butunlay ketdi, matni ("bos") qoldi. Amalda ro'yxatni imkon qadar tor qiling: izoh uchun 3–4 teg yetadi.
5.2 setHTML: brauzerning o'z tozalovchisi
XSS xavfi darsida setHTML bilan tanishgan edik. Uning ham sozlamasi bor — ruxsat etilgan teglar ro'yxati:
<div id="comment"></div>
<script>
const dirty = '<b>Osh</b> zo\'r! '
+ '<img src="x" onerror="console.log(1)">'
+ '<a href="javascript:alert(1)">bos</a>';
const box = document.getElementById("comment");
box.setHTML(dirty);
console.log(box.innerHTML);
box.setHTML(dirty, {
sanitizer: { elements: ["b", "a"], attributes: ["href"] },
});
console.log(box.innerHTML);
</script>Konsolda:
<b>Osh</b> zo'r! <a>bos</a>
<b>Osh</b> zo'r! <a>bos</a>Ikkinchi chaqiruvda biz a va href ga ruxsat berdik — lekin javascript: manzil baribir olib tashlandi. setHTML xavfli narsani hech qanday sozlama bilan o'tkazmaydi; buning uchun nomi "Unsafe" bilan tugaydigan alohida metod (setHTMLUnsafe) bor. Chrome 154 dagi natija. Qo'llash: Chrome va Edge 146+, Firefox 148+, Safari'da yo'q (web-features, 2026-oktabr). Shuning uchun hozircha amalda DOMPurify ko'proq.
Diqqat: Tozalangan HTML'ni keyin o'zgartirmang. "Tozaladim, endi
replacebilan ozgina tahrirlayman" — xavfli: o'zgartirilgan satr yana xavfli bo'lib qolishi mumkin (bunday hujumlar mXSS — mutation XSS deyiladi). Tozalash — sink'dan oldingi oxirgi qadam.
6. Brauzer darajasidagi himoya: CSP va Trusted Types
6.1 CSP: inline skriptni to'xtatish
Kodda xato bo'lishi mumkin — dasturchilar ham odam. Shuning uchun ikkinchi qatlam kerak: brauzerning o'zi xavfli narsani taqiqlasin. eval darsida CSP (Content Security Policy) bilan tanishgan edik: u 'unsafe-eval' siz eval ni to'xtatadi. XSS'ga qarshi uning asosiy kuchi — script-src: HTML ichiga yozilgan skriptlar va onerror kabi atributlar ishlamaydi.
O'zimizniki bo'lgan inline skript-chi? Unga nonce ("bir martalik raqam") beriladi: server har javobda yangi tasodifiy satr yasaydi, uni CSP'ga va o'z <script> lariga yozadi. Hujumchi bu raqamni oldindan bilmaydi:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy"
content="script-src 'nonce-r4nd0m'; object-src 'none'">
</head>
<body>
<p id="out"></p>
<script nonce="r4nd0m">
document.addEventListener("securitypolicyviolation", (e) => {
console.log(`To'sildi: ${e.violatedDirective}`);
});
console.log("1: nonce bor skript ishladi");
</script>
<script>
console.log("2: nonce'siz skript ishladi");
</script>
<script nonce="r4nd0m">
document.getElementById("out").innerHTML =
'<img src="x" onerror="console.log(\'3: XSS\')">';
</script>
</body>
</html>Konsolda:
1: nonce bor skript ishladi
To'sildi: script-src-elem
To'sildi: script-src-attrNonce'li skript ishladi. Nonce'siz — to'sildi (script-src-elem — <script> elementlari qoidasi). Uchinchi skriptda innerHTML xato — lekin hujum baribir ishlamadi: onerror atributi to'sildi (script-src-attr). securitypolicyviolation hodisasi har bir to'sishni xabar qiladi. Chrome konsolida esa to'liq xabar chiqadi:
Executing inline event handler violates the following Content Security Policy directive 'script-src 'nonce-r4nd0m''. Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. The action has been blocked.Tarjimasi: "HTML ichidagi hodisa ishlovchisini bajarish CSP qoidasini buzadi. Ruxsat uchun 'unsafe-inline', hash yoki nonce kerak. Hash'lar hodisa ishlovchilari, style atributlari va javascript: o'tishlarga qo'llanmaydi. Amal bloklandi." Nonce'siz <script> uchun Chrome xabarda shu skriptning tayyor hash'ini ham beradi — o'zgarmaydigan skriptga nonce o'rniga hash yozish mumkin.
Bu yerda nonce — darsdagi qattiq satr, chunki sahifa statik. Haqiqiy saytda u server tomonidan har javobda yangilanadi (server — keyingi qismlarda). CSP'ning qolgan direktivalari va saytga qanday qo'yilishi — keyingi darsda.
6.2 Trusted Types: sink'larni qulflash
CSP skriptni to'xtatadi, lekin innerHTML ga begona satr yozishning o'zini emas. Trusted Types ("ishonchli turlar") — bundan ham qattiqroq qoida: xavfli sink'lar oddiy satrni qabul qilmaydi. Ularga faqat siz yozgan siyosat (policy) dan o'tgan maxsus qiymat beriladi.
U CSP direktivasi bilan yoqiladi:
require-trusted-types-for 'script'; trusted-types bahorBirinchi qism — "sink'larga satr bermang", ikkinchisi — "faqat bahor nomli siyosat yaratish mumkin". Sinab ko'ramiz:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script';
trusted-types bahor">
</head>
<body>
<p id="out"></p>
<script>
const out = document.getElementById("out");
const comment = '<img src="x" onerror="console.log(\'XSS\')">Zo\'r';
try {
out.innerHTML = comment; // oddiy satr
} catch (error) {
console.log(error.message);
}
const policy = trustedTypes.createPolicy("bahor", {
createHTML: (input) => input
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">"),
});
out.innerHTML = policy.createHTML(comment);
console.log(out.innerHTML);
try {
setTimeout("console.log('satrdan kod')");
} catch (error) {
console.log(error.message);
}
</script>
</body>
</html>Konsolda:
Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment.
<img src="x" onerror="console.log('XSS')">Zo'r
Failed to execute 'setTimeout' on 'Window': This document requires 'TrustedScript' assignment.Tarjimasi: "innerHTML ni o'rnatib bo'lmadi: bu hujjat TrustedHTML talab qiladi." Oddiy satr — TypeError. Siyosatdan o'tgan qiymat esa qabul qilindi: bizning createHTML uni ekranladi va teg harflar bo'lib turibdi. Satrli setTimeout ham to'xtadi (TrustedScript kerak). Ko'ryapsizmi, endi xavfli joylarning hammasi bitta joyga — siyosat funksiyasiga — yig'ildi. Audit qilish uchun butun kodni emas, faqat siyosatlarni o'qish yetadi.
Siyosat ichida o'zingiz ekranlashingiz shart emas: DOMPurify RETURN_TRUSTED_TYPE: true sozlamasi bilan tayyor TrustedHTML qaytaradi. U o'z siyosatini dompurify nomi bilan yaratadi — CSP'da trusted-types dompurify bahor deb ruxsat berish kerak (biz Chrome 154 da shunday sinadik). setHTML esa Trusted Types talab qilmaydi — u o'zi xavfsiz sink.
Qo'llash: Chrome va Edge 83+, Firefox 148+, Safari 26+ — 2026 yil fevralidan Baseline (web-features). Mavjud katta loyihaga birdan yoqish qiyin: avval Content-Security-Policy-Report-Only sarlavhasi bilan "faqat xabar ber" rejimida qaysi joylar buzilishini yig'ishadi, keyin tuzatib, yoqishadi.
Tekshirib ko'ring: Sahifada Trusted Types yoqilgan. Sardor
el.textContent = commentyozdi. Xato chiqadimi?
Javob
Yo'q. textContent — sink emas: u matnni HTML deb o'qimaydi. Trusted Types faqat xavfli joylarni (innerHTML, script.src, eval…) qulflaydi. Xavfsiz API'lar avvalgidek ishlaydi — shu sababli textContent va createElement bilan yozilgan kod Trusted Types'ga hech o'zgartirishsiz o'tadi.
Hujumchi nigohi
Hujumchi vazifalar ilovasidagi har bir kirish nuqtasini sinaydi:
- Vazifa matni:
<img src=x onerror=alert(1)>— ro'yxatda,aria-labelda va o'chirish dialogida chiqadi. - JSON import:
"><svg onload=alert(2)> #<b>teg</b> #xss— tegga aylanadi deb umid qiladi. - Havola:
?filtr=%3Cscript%3E…— filtr nomi sahifaga chiqadi deb o'ylaydi.
Hammasi muvaffaqiyatsiz, chunki ilovada birorta ham sink yo'q: matn faqat textContent bilan, filtr — oq ro'yxat bilan (URL darsi), HTML'ni hech qayerda yasamaymiz. Kanon tekshiruvi buni brauzerda isbotlaydi: sahifada img va svg soni 0, birorta alert ham chiqmadi. Keyingi darsda buning ustiga CSP va Trusted Types qo'shiladi — kimdir kelajakda innerHTML yozib qo'ysa ham, brauzer uni to'xtatadi.
7. Ko'p uchraydigan xatolar
7.1 Xavfli so'zlarni o'chirish
text.replaceAll("<script>", "") — himoya emas. <img onerror>, <svg onload>, katta harflar, <scr<script>ipt> — chetlab o'tish yo'llari cheksiz. Tuzatish: textContent yoki ishonchli tozalovchi.
7.2 Noto'g'ri joyda ekranlash
HTML ekranlash faqat qiymat matn yoki qo'shtirnoqli atribut ichiga tushganda ishlaydi. Uch joyda u yetmaydi (Tagged template literals darsida birinchi marta ko'rgan edik):
hrefvasrc—javascript:alert(1)da ekranlanadigan belgi yo'q.- Qo'shtirnoqsiz atribut —
<td class=${sinf}>da bo'sh joy yangi atribut ochadi:x onclick=…. <script>va<style>ichi — u yerda HTML emas, JavaScript va CSS qoidalari ishlaydi.
Tuzatish: har joyning o'z qoidasi — matn uchun textContent, atribut uchun setAttribute yoki xususiyat (el.title = …), URL uchun protokol tekshiruvi. <script> ichiga begona qiymatni umuman qo'ymang.
7.3 "Bu ma'lumot o'zimizning serverdan"
Serverdagi ma'lumotni mehmonlar yozgan. Stored XSS aynan shu ishonchdan boshlanadi. Tuzatish: begona matn — qayerdan kelmasin — begona.
7.4 Tozalangan HTML'ni keyin o'zgartirish
mXSS xavfi. Tuzatish: tozalash — sink'dan oldingi oxirgi qadam.
7.5 Tozalash uchun innerHTML = ""
Bo'sh satr xavfsiz, lekin kodda innerHTML qolsa, keyin kimdir unga o'zgaruvchi qo'yib yuboradi va ESLint qoidasi ham bu ikkisini farqlay olmaydi. Tuzatish: element ichini tozalash uchun el.replaceChildren().
8. Mashqlar
1-mashq (oson): Sink'larni toping
Quyidagi qatorlardan qaysilari sink va qaysilari xavfsiz?
card.textContent = taom.nom; // A
card.innerHTML = `<b>${taom.nom}</b>`; // B
link.href = mehmon.sayt; // C
setTimeout(yangila, 1000); // D
setTimeout("yangila()", 1000); // E
img.alt = mehmon.ism; // FSink'lar:
Yechim
- B — HTML sink:
taom.nomichida teg bo'lsa, u HTML bo'ladi. - C — URL sink:
javascript:manzil bo'lishi mumkin; protokolni tekshirish kerak. - E — JavaScript sink: satr kod sifatida bajariladi (
evaldarsi). - A, D, F — xavfsiz:
textContentvaalt— oddiy matn,setTimeout(yangila)— funksiya, satr emas.
2-mashq (o'rta): Xavfsiz profil havolasi
renderLink(website) funksiyasini yozing: <a> element qaytarsin. Manzil xavfsiz bo'lsa (isSafeUrl — «Tekshiruv: faqat ruxsat etilgan protokollar» bo'limidan) — href o'rnatilsin, matni — manzilning hostname i. Xavfsiz bo'lmasa — havola emas, span va "Noto'g'ri havola" matni.
Yechim
<ul id="links"></ul>
<script>
function isSafeUrl(input) {
try {
const url = new URL(input, "https://example.com/");
return url.protocol === "https:" || url.protocol === "http:";
} catch {
return false;
}
}
function renderLink(website) {
if (!isSafeUrl(website)) {
const span = document.createElement("span");
span.textContent = "Noto'g'ri havola";
return span;
}
const link = document.createElement("a");
link.href = website;
const base = "https://example.com/";
link.textContent = new URL(website, base).hostname;
link.rel = "noopener";
return link;
}
const list = document.getElementById("links");
for (const site of ["https://t.me/sizning_kanal",
"javascript:alert(1)"]) {
const li = document.createElement("li");
li.append(renderLink(site));
list.append(li);
console.log(li.textContent);
}
</script>Konsolda:
t.me
Noto'g'ri havolaXavfli manzil href ga umuman tushmadi. rel="noopener" — target="_blank" qo'shilsa ham himoya joyida tursin.
3-mashq (qiyin): DOMPurify + Trusted Types
Trusted Types yoqilgan sahifada izohni HTML sifatida chiqaring: qalin va kursivga ruxsat, qolgani tozalansin. Siyosat nomi — bahor, uning createHTML i DOMPurify'ni chaqirsin (ALLOWED_TAGS: ["b", "i"]).
Ishora: DOMPurify o'z siyosatini ham yaratadi — CSP'dagi ro'yxatga dompurify ni qo'shing.
Yechim
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script';
trusted-types dompurify bahor">
</head>
<body>
<div id="comment"></div>
<script type="module">
import DOMPurify
from "https://cdn.jsdelivr.net/npm/dompurify@3.4.16/+esm";
const policy = trustedTypes.createPolicy("bahor", {
createHTML: (input) => DOMPurify.sanitize(input, {
ALLOWED_TAGS: ["b", "i"],
ALLOWED_ATTR: [],
}),
});
const dirty = '<b>Zo\'r!</b> <i>tavsiya</i>'
+ '<img src=x onerror="console.log(\'XSS\')">';
const box = document.getElementById("comment");
box.innerHTML = policy.createHTML(dirty);
console.log(box.innerHTML);
</script>
</body>
</html>Konsolda:
<b>Zo'r!</b> <i>tavsiya</i>Endi sahifadagi har bir innerHTML majburan bahor siyosatidan o'tadi. Kimdir ertaga box.innerHTML = dirty yozsa — TypeError, va xato darhol ko'rinadi.
4-mashq: Vazifalar qadami — XSS auditi
vazifalar foydalanuvchi matnini ko'rsatadi: vazifalar, teglar, import qilingan JSON, URL'dagi filtr. Bu qadamda kod o'zgarmaydi — avval audit qilamiz, keyin himoyani ESLint bilan qotiramiz.
1. Branch va audit.
git switch -c chore/xss-audit
grep -rnE \
-e "innerHTML|outerHTML|insertAdjacentHTML|document\.write" \
-e "eval\(|new Function|javascript:|srcdoc|setAttribute\(" \
assets/js sw.jsNatija: sink — 0. Topilganlar tekshirildi:
setAttribute— faqataria-*atributlar uchun. Foydalanuvchi matniaria-labelda — bu atribut qiymati, u bajarilmaydi.havola.href— o'zimiz yasaganblob:manzil (eksport fayli,FilevaBlobdarsidagi qadam).querySelectorichidagi${…}— raqamli id va ichki amal nomi, foydalanuvchi matni emas.- URL'dagi
filtr— oq ro'yxat (11-qism). vendor/web-vitals.jsda ham sink yo'q.target="_blank"havola yo'q.
2. Himoyani qotirish — eslint.config.js ga yangi blok (assets/js va sw.js uchun):
{
// XSS himoyasi (13/#31): matnni HTML yoki kod qilib
// bajaradigan joylar (sink) taqiqlanadi.
// Foydalanuvchi matni — faqat textContent
files: ["assets/js/**/*.js", "sw.js"],
rules: {
"no-eval": "error",
"no-implied-eval": "error", // setTimeout("kod")
"no-new-func": "error",
"no-script-url": "error", // "javascript:..."
"no-restricted-properties": [
"error",
{
object: "document",
property: "write",
message: "XSS: textContent ishlating.",
},
{
property: "insertAdjacentHTML",
message: "XSS: insertAdjacentElement yoki append ishlating.",
},
],
"no-restricted-syntax": [
"error",
{
selector: "AssignmentExpression > MemberExpression.left"
+ "[property.name=/^(innerHTML|outerHTML)$/]",
message: "XSS: innerHTML/outerHTML o'rniga "
+ "textContent yoki createElement.",
},
],
},
},(Kanonda selector, message va izoh qatorlari uzunroq; telefon ekraniga sig'ishi uchun bu yerda bo'lindi.)
Birinchi to'rttasi — ESLint'ning o'z qoidalari (ESLint va Prettier). no-restricted-properties — ma'lum xususiyatni taqiqlaydi. no-restricted-syntax eng kuchlisi: kodning daraxtidan (AST, JS dvigateli ichida) "innerHTML ga tayinlash" shaklini qidiradi. O'qish (el.innerHTML ni konsolga chiqarish) taqiqlanmaydi — faqat yozish.
Qoidalar ishlashini sinov fayli bilan tekshirdik: el.innerHTML = …, insertAdjacentHTML, document.write, setTimeout("alert(1)") → ✖ 4 problems (4 errors).
3. Isbot — manfiy nazorat. Himoya ishlayaptimi? Buni bilishning yo'li — uni ataylab buzish. Ilovaning Web Vitals qadamidagi nusxasida yorliq.textContent ni yorliq.innerHTML ga almashtirdik va keshga <img src=x onerror=alert(1)> matnli vazifa qo'ydik:
buzilgan: label: "" img: 1 alert: ["1"]
kanon: label: "<img src=x onerror=alert(1)>" img: 0 alert: []
eslint (buzilgan render.js): 75:3 error XSS: innerHTML/outerHTML o'rniga textContent yoki createElement no-restricted-syntaxBuzilgan versiyada hujum ishladi (alert chiqdi), kanonda — matn harflar bo'lib turdi. ESLint esa buzilgan qatorni darhol topdi: endi innerHTML qaytib kirsa, npm run lint yiqiladi.
4. Brauzer tekshiruvi — 111/111 (+8 XSS tekshiruvi, keyingi har qadamda ham qayta ishlaydi): qo'shilgan vazifa, aria-label, JSON import ("><svg onload=alert(2)> #<b>teg</b> #xss), teglar (Teglar: #xss — 1), dialog matni, ?filtr=%3Cscript%3E… — hammasi matn; img/svg — 0, alert — 0. Testlar 90/90.
5. Commit va PR. Sarlavha 72 belgidan oshmasin, tafsilot — tanada:
git add eslint.config.js
git commit \
-m "chore: XSS auditi — xavfli sink'lar ESLint bilan taqiqlandi" \
-m "Foydalanuvchi matni faqat textContent orqali."
gh pr create --fill
gh pr merge --mergeDiff: 1 fayl, +32. Kod o'zgarmadi — lekin endi u o'zgarishdan himoyalangan.
9. Real ishda
- Freymvorklar. React matnni sukut bo'yicha ekranlaydi; xavfli joy —
dangerouslySetInnerHTML(nomi ataylab qo'rqinchli). Lit —htmltegi bilan (Tagged template literals). React'dagi XSS tuzoqlari — 18-qismda. - Trusted Types amalda. Uni Google jamoasi ishlab chiqqan va o'z xizmatlarida qo'llaydi. 2026 yildan u barcha asosiy brauzerlarda bor — yangi loyihada boshidan yoqish oson.
- Bug bounty ("xato uchun mukofot"). Ko'p kompaniyalar o'z saytidan zaiflik topib, xabar bergan odamga pul to'laydi. XSS — shunday topiladigan eng ko'p zaifliklardan biri.
- Intervyu. "XSS turlari?", "Source va sink nima?", "
innerHTMLo'rniga nima?", "CSP va Trusted Types qanday himoya qiladi?" — frontend xavfsizlik savollarining asosi.
Xulosa
- XSS — begona matn sizning saytingizda kod bo'lib ishlashi; stored, reflected va DOM turlari bor, oxirgi qaror doim JavaScript'ning sink'ida.
- Source → sink: begona qiymat
innerHTML,eval,hrefkabi joylarga tekshirilmasdan tushmasin. - Matn —
textContent; URL —new URLbilan protokol oq ro'yxati; HTML — DOMPurify yokisetHTML, tozalash — oxirgi qadam. - HttpOnly cookie'ni himoya qiladi, lekin XSS'ni emas;
target="_blank"zamonaviy brauzerda o'zinoopener. - CSP (
script-src, nonce) inline kodni, Trusted Types esa sink'larga oddiy satr berishni to'xtatadi.
Keyingi dars: Brauzer xavfsizlik modeli — same-origin, CORS, CSP'ning to'liq tuzilmasi, COOP/COEP, sandbox va Permissions-Policy; vazifalar ga CSP va Trusted Types qo'yamiz.
Manbalar
- OWASP: "Cross Site Scripting Prevention Cheat Sheet", "DOM based XSS Prevention Cheat Sheet" — cheatsheetseries.owasp.org
- web.dev: "Prevent DOM-based cross-site scripting vulnerabilities with Trusted Types" — web.dev/articles/trusted-types
- DOMPurify README (3.4.16) — github.com/cure53/DOMPurify
- MDN:
Element.setHTML(), Trusted Types API, CSPscript-src— developer.mozilla.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!