Express web xavfsizligi
Ilovangizda endi foydalanuvchi hisobi, parol va fayl bor. Shu bilan birga xavf ham paydo bo’ldi: oldin eng yomoni sinov ma’lumotining yo’qolishi edi, endi esa birovning hisobiga kirib olish mumkin.
Bu darsda beshta amaliy himoya qatlamini qo’shamiz. Nazariy tomoni amaliy web xavfsizligi darsida yoritilgan — bu yerda kod yozamiz.
XSS: foydalanuvchi matni kod bo’lib qolganda
Hujum yo’li oddiy: vazifa sarlavhasiga matn o’rniga teg yoziladi.
const ejs = require("ejs");
const title = "<script>alert(1)</script>";
console.log("Ekranlangan:", ejs.render("<li><%= title %></li>", { title }));
console.log("Xom:", ejs.render("<li><%- title %></li>", { title }));
node ejs-escape.js
Ekranlangan: <li><script>alert(1)</script></li>
Xom: <li><script>alert(1)</script></li>
Farq bitta belgida, oqibat esa katta.
| Yozuv | Nima qiladi |
|---|---|
<%= %> |
Maxsus belgilarni ekranlaydi — matn matn bo’lib qoladi |
<%- %> |
HTML ni xom holda qo’yadi — teg bajariladi |
Birinchi holatda <script> — brauzer buni matn deb ko’rsatadi,
foydalanuvchi ekranda <script>alert(1)</script> yozuvini o’qiydi.
Ikkinchisida esa haqiqiy skript sahifaga tushdi va ishga tushadi.
Tekshirish usuli oddiy: ilovangizga <b>qalin</b> deb vazifa qo’shing.
Ro’yxatda shu matn o’z holicha ko’rinsa — himoya ishlayapti. Qalin
harflar chiqsa — <%- %> yozilgan joyni qidiring.
CSRF: begona saytdan kelgan so’rov
Hujum shunday ko’rinadi: siz saytingizga kirgansiz, keyin boshqa
sahifaga o’tasiz va u sahifa fon rejimida sizning saytingizga
POST /tasks/5/delete yuboradi. Siz hech narsa bosmadingiz, lekin
vazifa o’chdi.
Birinchi himoya allaqachon bor —
sessiya darsida qo’yilgan
sameSite: "lax". U cookie’ni boshqa saytdan kelgan POST so’roviga
qo’shmaydi.
Ikkinchi qatlam — token:
const crypto = require("crypto");
// Sessiyada token bo'lmasa yaratamiz
function csrfToken(request, response, next) {
if (!request.session.csrfToken) {
request.session.csrfToken = crypto.randomBytes(32).toString("hex");
}
// Shablonlar uchun ochiq qilamiz
response.locals.csrfToken = request.session.csrfToken;
next();
}
// Holatni o'zgartiradigan so'rovlarni tekshiradi
function checkCsrf(request, response, next) {
const sent = request.body._csrf;
if (!sent || sent !== request.session.csrfToken) {
return response.status(403).json({ error: "So'rov tasdiqlanmadi" });
}
next();
}
module.exports = { csrfToken, checkCsrf };
Formaga yashirin maydon qo’shiladi:
<form action="/tasks" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>" />
<input type="text" name="title" />
<button type="submit">Qo'shish</button>
</form>
Nega bu ishlaydi: hujumchining sayti sizning sessiyangizdagi tokenni o’qiy olmaydi. Cookie avtomatik yuboriladi, token esa yo’q — server so’rovni rad etadi.
helmet: HTTP sarlavhalari
npm install helmet
const helmet = require("helmet");
app.use(helmet());
Bitta qator o’nlab himoya sarlavhasini qo’shadi:
curl -i http://localhost:3000/
HTTP/1.1 200 OK
Content-Security-Policy: default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Eng muhim uchtasi:
| Sarlavha | Nima qiladi |
|---|---|
Content-Security-Policy |
Skript faqat o’z domeningizdan yuklanadi — XSS ta’sirini kamaytiradi |
X-Frame-Options |
Sahifangizni begona saytga iframe qilib joylashtirishni to’sadi |
X-Content-Type-Options |
Brauzer fayl turini “taxmin qilishini” o’chiradi |
Rate limiting: urinishlarni cheklash
Kirish marshruti himoyasiz bo’lsa, hujumchi soatiga minglab parol sinaydi.
npm install express-rate-limit
const rateLimit = require("express-rate-limit");
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 daqiqa
limit: 5,
message: {
error: "Juda ko'p urinish. 15 daqiqadan keyin qayta urinib ko'ring",
},
standardHeaders: "draft-7",
legacyHeaders: false,
});
app.post("/login", loginLimiter, loginHandler);
Bu yerdagi loginHandler —
kirish darsida yozilgan marshrut funksiyasi.
Ketma-ket olti marta so’rang:
1-urinish: HTTP/1.1 401 Unauthorized
2-urinish: HTTP/1.1 401 Unauthorized
3-urinish: HTTP/1.1 401 Unauthorized
4-urinish: HTTP/1.1 401 Unauthorized
5-urinish: HTTP/1.1 401 Unauthorized
6-urinish: HTTP/1.1 429 Too Many Requests
Oltinchi so’rov marshrutga umuman yetib bormadi. 429 Too Many Requests
— aynan shu holat uchun mo’ljallangan kod.
Sirlarni .env da saqlash
Kodda qolgan maxfiy qiymatlarni yig’ib olamiz: sessiya kaliti, JWT kaliti, tashqi xizmatlar kalitlari.
Node.js 20 dan beri .env faylini o’qish uchun paket kerak emas:
SESSION_SECRET=bu-yerga-uzun-tasodifiy-qator-yozing
JWT_SECRET=bu-yerga-boshqa-uzun-qator-yozing
NODE_ENV=development
node --env-file=.env app.js
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
},
})
);
Kalit yetishmasa, server ishga tushmasin:
const requiredVariables = ["SESSION_SECRET", "JWT_SECRET"];
for (const name of requiredVariables) {
if (!process.env[name]) {
console.error(`Muhit o'zgaruvchisi yo'q: ${name}`);
process.exit(1);
}
}
Nega shunday: standart qiymat qo’yilsa (|| "sinov-kalit"), serverga
.env ni ko’chirish unutilganda ilova ishlayveradi — lekin hamma
biladigan kalit bilan. Darhol to’xtash xavfsizroq.
npm audit
O’rnatgan paketlaringizda ham zaiflik topilishi mumkin:
npm audit
found 0 vulnerabilities
Zaiflik topilsa, ro’yxat va jiddiylik darajasi ko’rsatiladi. Ko’pincha yechim — yangilash:
npm audit fix
Himoya qatlamlari jadvali
Butun darsni bir joyga yig’amiz:
| Hujum | Himoya | Qayerda |
|---|---|---|
| XSS | <%= %>, textContent, CSP |
Shablon va frontend |
| CSRF | sameSite, token |
Sessiya va forma |
| Sessiya o’g’irlash | httpOnly, secure |
Cookie sozlamasi |
| Parol tanlash | Rate limiting | Kirish marshruti |
| SQL injection | Parametrli so’rov | Model qatlami |
| Sirlarning sizishi | .env, .gitignore |
Loyiha sozlamasi |
Bitta qatlam yetarli emas — har biri boshqasi ishlamay qolganda ushlab turadi.
Amaliy mashq
task-app loyihasida davom eting:
- Vazifa sarlavhasiga
<b>qalin</b>yozib qo’shing va ro’yxatda matn o’z holicha ko’rinishini tasdiqlang; - Shablonda bitta joyni ataylab
<%- %>ga o’zgartirib, tegning bajarilishini ko’ring — so’ng darhol qaytaring; helmet()ni ulab,curl -ibilan yangi sarlavhalarni ko’ring;express-rate-limitni kirish marshrutiga qo’shib, olti marta ketma-ket so’rang va429javobini oling;- Sessiya va JWT kalitlarini
.envga ko’chirib,--env-filebilan ishga tushiring; .envni.gitignorega qo’shib,git statusda ko’rinmasligini tekshiring.
2-qadam XSS ning butun mohiyatini bir necha soniyada ko’rsatadi: bitta belgi farqi matnni bajariladigan kodga aylantiradi.
Qisqacha xulosa
<%= %>ekranlaydi,<%- %>xom HTML qo’yadi — foydalanuvchi ma’lumoti faqat birinchisi orqali chiqariladi.- CSRF’ga qarshi
sameSite: "lax"va formadagi yashirin token birga ishlaydi. Authorizationsarlavhasi bilan ishlaydigan API’da CSRF muammosi yo’q.helmet()bitta qatorda o’nlab himoya sarlavhasini qo’shadi; CSP tashqi skriptlarni to’sadi va uni ataylab sozlash kerak.express-rate-limitparol tanlash hujumini to’sadi va429qaytaradi.- Maxfiy kalitlar kodda emas,
.envda;.envesa.gitignoreda — kalit yetishmasa server ishga tushmasligi kerak. npm auditpaketlardagi zaiflikni ko’rsatadi;--forceni o’ylab ishlating.
Keyingi darsda API testlash — marshrutlarni qo’lda emas, avtomatik sinash.