18-bo‘lim
Xavfsizlik
XSS va chiqishni ekranlash, yo'ldan chiqish hujumi, so'rovlarni cheklash, xavfsizlik sarlavhalari, maxfiy ma'lumot va bog'liqliklar xavfi.
Ushbu bo‘lim mundarijasi
Oldingi bo'limlarda xavfsizlik haqida yo'l-yo'lakay gapirdik. Bu bo'limda qolganlarini bir joyga yig'amiz.
Asosiy tamoyil bitta: tashqaridan kelgan hech narsaga ishonmang.
XSS - chiqishni ekranlash #
function ekranla(matn) {
return String(matn)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
const yomon = '<script>alert("xato")</script>';
console.log("Xom:", yomon);
console.log("Ekranlangan:", ekranla(yomon));
console.log("Skript qoldimi:", ekranla(yomon).includes("<script>"));
Xom: <script>alert("xato")</script>
Ekranlangan: <script>alert("xato")</script>
Skript qoldimi: false
Tasavvur qiling: izohlar bo'limi bor va kimdir izoh sifatida yuqoridagi matnni yozdi.
Agar uni sahifaga shundayligicha qo'ysangiz, brauzer uni matn emas, kod deb biladi va ishga tushiradi. Skript esa boshqa foydalanuvchilarning cookie sini o'g'irlashi mumkin.
Bu XSS (Cross-Site Scripting) deb ataladi.
Himoya ikki qavatli:
- Chiqishni ekranlang - yuqoridagi kabi. Zamonaviy shablon
dvigatellari buni avtomatik qiladi;
innerHTMLesa qilmaydi. BrauzerdatextContentishlating. httpOnlycookie (17-bo'lim) - skript sessiyani o'qiy olmaydi.
Muhim nozik joy: ekranlash chiqishda bo'ladi, kiritishda emas. Ma'lumotni bazaga asl holida saqlang - chunki u JSON ga, elektron pochtaga yoki CSV ga ham chiqishi mumkin va har birining ekranlash qoidasi boshqa.
Yo'ldan chiqish hujumi #
import { join, resolve, sep } from "node:path";
const ildiz = resolve("/var/www/ommaviy");
function xavfliYol(soralgan) {
return join(ildiz, soralgan);
}
function xavfsizYol(soralgan) {
const toliq = resolve(ildiz, soralgan);
if (toliq !== ildiz && !toliq.startsWith(ildiz + sep)) {
return null;
}
return toliq;
}
const yaxshi = "rasmlar/logo.png";
const yomon = "../../../etc/parol";
console.log("Xavfli, yaxshi so'rov:", xavfliYol(yaxshi));
console.log("Xavfli, yomon so'rov:", xavfliYol(yomon));
console.log("Xavfsiz, yaxshi so'rov:", xavfsizYol(yaxshi));
console.log("Xavfsiz, yomon so'rov:", xavfsizYol(yomon));
Xavfli, yaxshi so'rov: /var/www/ommaviy/rasmlar/logo.png
Xavfli, yomon so'rov: /etc/parol
Xavfsiz, yaxshi so'rov: /var/www/ommaviy/rasmlar/logo.png
Xavfsiz, yomon so'rov: null
Ikkinchi qatorga qarang: join .. larni hisoblab, yo'lni
papkadan butunlay chiqarib yubordi. Natijada tizim fayli
ochildi.
Bu yo'ldan chiqish (path traversal) hujumi. 12-bo'limdagi joker belgili marshrut aynan shunga ochiq edi.
Himoya uch qadam:
- So'ralgan yo'lni normallashtiring;
- Ildizga nisbatan to'liq yo'lga aylantiring;
- Natija ildiz ichida qolganini tekshiring.
Eng ishonchli yechim esa - foydalanuvchi bergan matnni yo'l qilib ishlatmaslik. Fayllarni bazadagi identifikator bo'yicha toping, nom bo'yicha emas.
Statik fayllar uchun express.static ni ishlating - u bu
tekshiruvni o'zi qiladi.
So'rovlarni cheklash #
import express from "express";
const app = express();
function cheklov({ oyna, chegara }) {
const urinishlar = new Map();
let soat = 0;
const qatlam = (sorov, javob, keyingi) => {
const kalit = sorov.ip ?? "nomalum";
const yozuv = urinishlar.get(kalit) ?? { soni: 0, boshi: soat };
if (soat - yozuv.boshi >= oyna) {
yozuv.soni = 0;
yozuv.boshi = soat;
}
yozuv.soni++;
urinishlar.set(kalit, yozuv);
if (yozuv.soni > chegara) {
javob.status(429).json({ xato: "juda ko'p so'rov" });
return;
}
keyingi();
};
qatlam.soatniSur = (n) => { soat += n; };
return qatlam;
}
const cheklagich = cheklov({ oyna: 60, chegara: 3 });
app.use(cheklagich);
app.get("/", (s, j) => j.json({ holat: "yaxshi" }));
const server = app.listen(0);
const port = server.address().port;
const asos = `http://localhost:${port}/`;
const holatlar = [];
for (let i = 0; i < 5; i++) {
holatlar.push((await fetch(asos)).status);
}
console.log("Besh so'rov:", holatlar.join(", "));
cheklagich.soatniSur(61);
console.log("Oyna tugagach:", (await fetch(asos)).status);
server.close();
Besh so'rov: 200, 200, 200, 429, 429
Oyna tugagach: 200
429 Too Many Requests - "juda ko'p so'radingiz" degan holat kodi.
Cheklovsiz kirish shakli - parolni taxmin qilish uchun ochiq eshik. Hujumchi soniyada minglab parolni sinab ko'ra oladi.
Kamida ikki joyni cheklang:
- Kirish va parolni tiklash - IP va login bo'yicha, qattiq chegara bilan.
- Butun API - umumiy, kengroq chegara bilan.
Yuqoridagi misol soddalashtirilgan: u xotirada saqlaydi, demak
server qayta ishga tushsa yoki bir nechta server bo'lsa
ishlamaydi. Haqiqiy loyihada Redis yoki express-rate-limit
paketi ishlatiladi.
Diqqat: sorov.ip teskari proksi ortida noto'g'ri bo'lishi
mumkin. Bunday holatda app.set("trust proxy", 1) kerak - lekin
uni faqat ishonchli proksi ortida yozing.
Xavfsizlik sarlavhalari #
import express from "express";
const app = express();
app.disable("x-powered-by");
app.use((sorov, javob, keyingi) => {
javob.set({
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Referrer-Policy": "no-referrer",
"Content-Security-Policy": "default-src 'self'",
});
keyingi();
});
app.get("/", (s, j) => j.json({ holat: "yaxshi" }));
const server = app.listen(0);
const port = server.address().port;
const javob = await fetch(`http://localhost:${port}/`);
console.log("x-powered-by:", javob.headers.get("x-powered-by"));
console.log("nosniff:", javob.headers.get("x-content-type-options"));
console.log("frame:", javob.headers.get("x-frame-options"));
console.log("csp bor:", javob.headers.has("content-security-policy"));
server.close();
x-powered-by: null
nosniff: nosniff
frame: DENY
csp bor: true
Birinchi qator null - va bu aynan kerak edi.
Odatiy holda Express har bir javobga X-Powered-By: Express
sarlavhasini qo'shadi. U hech qanday foyda bermaydi, lekin
hujumchiga qaysi texnologiya ishlatilayotganini bepul aytib
beradi. Shundan keyin u aynan Express ning ma'lum zaifliklarini
qidira boshlaydi.
app.disable("x-powered-by") uni butunlay olib tashlaydi.
Bu "noaniqlik orqali xavfsizlik" - u o'z-o'zidan himoya emas, lekin bepul va foydasi bor.
| Sarlavha | Nimadan himoya qiladi |
|---|---|
X-Content-Type-Options: nosniff | Brauzer fayl turini taxmin qilishidan |
X-Frame-Options: DENY | Sahifani begona saytga joylashdan |
Referrer-Policy | Manzil boshqa saytga sizib chiqishidan |
Content-Security-Policy | Begona skript ishlashidan |
Amalda bularni qo'lda yozmasdan, helmet paketi ishlatiladi -
u oqilona odatiy qiymatlarni beradi.
Maxfiy ma'lumot #
const sozlama = {
port: process.env.PORT ?? "3000",
bazaUrl: process.env.BAZA_URL ?? "sqlite::memory:",
maxfiyKalit: process.env.MAXFIY_KALIT ?? null,
};
function xavfsizKorinish(s) {
return {
...s,
maxfiyKalit: s.maxfiyKalit ? "***" : null,
};
}
console.log(xavfsizKorinish(sozlama));
if (!sozlama.maxfiyKalit) {
console.log("Ogohlantirish: MAXFIY_KALIT berilmagan");
}
{ port: '3000', bazaUrl: 'sqlite::memory:', maxfiyKalit: null }
Ogohlantirish: MAXFIY_KALIT berilmagan
Eng ko'p uchraydigan sizib chiqish yo'li - jurnal fayllari.
console.log("Sozlama:", sozlama); // kalit jurnalda
console.log("So'rov:", sorov.headers); // cookie va token jurnalda
console.error(xato); // xatoda parol bo'lishi mumkin
Jurnal ko'pincha uchinchi tomon xizmatiga yuboriladi, uzoq saqlanadi va unga ko'p odam kirishi mumkin.
Qoidalar:
- Butun obyektni jurnalga tashlamang - kerakli maydonlarni tanlab yozing.
- Maxfiy maydonlarni niqoblang (yuqoridagi
***). - Mijozga qaytadigan xatoda tafsilot bo'lmasin (10-bo'lim).
.envni git ga qo'shmang (2-bo'lim).
Va eng muhimi: kalit tasodifan git ga tushsa, uni o'chirish yetarli emas - u tarixda qoladi. Kalitni almashtirish kerak.
Bog'liqliklar #
4-bo'limda aytilgan edi: har bir npm install - begona kodga
ishonch.
Amaliy choralar:
npm auditni muntazam ishlating.package-lock.jsonvanpm ci- kutilmagan versiya kelib qolmaydi.- Paket qo'shishdan oldin standart kutubxonani tekshiring.
Node da bugun
fetch, test yurituvchi,.envo'qish va SQLite bor. npm installskriptlarni ishga tushirishi mumkin. Ishonchsiz paket uchun--ignore-scriptsbayrog'i bor.- Bog'liqliklarni kam saqlang. Har bir olib tashlangan paket - kamaygan xavf.
Qisqa ro'yxat #
| Xavf | Himoya | Bo'lim |
|---|---|---|
| SQL in'yeksiya | Tayyorlangan so'rovlar | 16 |
| XSS | Chiqishni ekranlash, httpOnly | 18, 17 |
| CSRF | sameSite cookie | 17 |
| Yo'ldan chiqish | Yo'lni tekshirish | 18 |
| Parol o'g'irlash | scrypt + tuz | 17 |
| Ommaviy tayinlash | Ruxsat etilganlar ro'yxati | 14 |
| Katta so'rov | limit va 413 | 10, 13 |
| Taxmin qilish | So'rovlarni cheklash | 18 |
| Ma'lumot sizishi | Xato tafsilotini yashirish | 10 |
- HTML ekranlaydigan funksiya yozing.
<script>teg qolmasligini tekshiring.joinbilan../../..ni sinab ko'ring.- Yo'lni ildiz ichida ekanini tekshiradigan funksiya yozing.
- So'rovlarni cheklovchi qatlam yozing.
- 429 holat kodini qaytaring.
x-powered-byni o'chiring.- To'rtta xavfsizlik sarlavhasini qo'ying.
- Maxfiy kalitni niqoblab chiqaring.
npm auditni ishga tushiring.
Xulosa #
- Tashqaridan kelgan hech narsaga ishonmang.
- XSS dan himoya - chiqishni ekranlash va
httpOnlycookie; ekranlash kiritishda emas, chiqishda. joinyo'ldan chiqishdan himoya qilmaydi - natija ildiz ichidami, tekshiring.- Kirish sahifasini albatta cheklang;
429qaytaring. x-powered-byni o'chiring va xavfsizlik sarlavhalarini qo'ying.- Maxfiy ma'lumotni jurnalga yozmang; sizib chiqqan kalitni almashtiring.
- Bog'liqliklarni kam saqlang va
npm ciishlating.
Keyingi bo'limda testlarni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.