Helmet va CORS sozlash
Muammodan boshlaylik
Ikki muammo:
1. CORS. Frontend http://localhost:5173 da, backend http://localhost:3000 da. Frontend so'rov yuborganida brauzer bloklab qo'ydi: Cross-Origin Request Blocked. Boshqa domendan kelgan so'rov — brauzer xavfsizlik siyosati.
2. Xavfsizlik headerlari. X-Powered-By: Express — xaker qaysi framework ekanini bildi. Keyingi hujum maqsadliroq bo'ldi. Onlayn skaner saytingizda Clickjacking, MIME sniffing zaifliklarini topdi.
Helmet va CORS — ikkisini bir darsda hal qilamiz.
Helmet — xavfsizlik headerlari
Helmet — HTTP response headerlariga xavfsizlik sozlamalarini qo'shadigan middleware.
npm install helmet
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import helmet from 'helmet';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(helmet()); // ← barcha Helmet headerlarini qo'shadi
await app.listen(3000);
}
Helmet qo'shadigan headerlar:
Content-Security-Policy — qaysi resurslar yuklana olishini belgilaydi
X-DNS-Prefetch-Control — DNS prefetch ni boshqaradi
X-Frame-Options: SAMEORIGIN — Clickjacking dan himoya
X-Content-Type-Options — MIME sniffing dan himoya
Referrer-Policy — referrer ma'lumotini cheklaydi
Strict-Transport-Security — faqat HTTPS (HSTS)
X-Powered-By — olib tashlanadi (framework yashiriladi)
Sozlash
app.use(
helmet({
// Content Security Policy — qaysi domenlardan resurs yuklash mumkin
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"], // inline JS ruxsat
styleSrc: ["'self'", "'unsafe-inline'", 'fonts.googleapis.com'],
imgSrc: ["'self'", 'data:', '*.amazonaws.com'],
connectSrc: ["'self'", 'api.myapp.uz'],
fontSrc: ["'self'", 'fonts.gstatic.com'],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
},
// HSTS — faqat HTTPS (development da o'chiriladi)
strictTransportSecurity: process.env.NODE_ENV === 'production'
? { maxAge: 31536000, includeSubDomains: true }
: false,
// Clickjacking — iframe da ko'rsatish
frameguard: { action: 'sameorigin' }, // faqat bir xil domen
}),
);
CORS — Cross-Origin Resource Sharing
CORS — brauzer xavfsizlik mexanizmi. Boshqa domendan kelgan so'rovlarni brauzer bloklab qo'yadi. Server "men bu domenga ruxsat beraman" desa — brauzer o'tkazadi.
// src/main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Oddiy sozlash — development uchun
app.enableCors({
origin: 'http://localhost:5173', // faqat shu frontend
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // cookie va Authorization header uchun muhim
});
await app.listen(3000);
}
Ko'p domen — whitelist
// src/main.ts
const ALLOWED_ORIGINS = [
'http://localhost:5173', // dev frontend
'http://localhost:3001', // dev admin panel
'https://myapp.uz', // prod frontend
'https://admin.myapp.uz', // prod admin
];
app.enableCors({
origin: (origin, callback) => {
// origin yo'q bo'lsa (Postman, server-to-server) — ruxsat
if (!origin) return callback(null, true);
if (ALLOWED_ORIGINS.includes(origin)) {
callback(null, true); // ← ruxsat
} else {
callback(new Error(`CORS: ${origin} ruxsat yo'q`)); // ← blok
}
},
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
credentials: true,
maxAge: 86400, // Preflight so'rovni 24 soat keshlaymiz
});
Environment bo'yicha sozlash
// src/main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = app.get(ConfigService);
const isDev = config.get('NODE_ENV') !== 'production';
app.use(helmet({
contentSecurityPolicy: isDev ? false : { /* prod CSP */ },
strictTransportSecurity: isDev ? false : { maxAge: 31536000 },
}));
app.enableCors({
origin: isDev
? true // dev — hammaga ruxsat
: [config.get('FRONTEND_URL'), // prod — whitelist
config.get('ADMIN_URL')],
credentials: true,
});
await app.listen(config.get('PORT', 3000));
}
Tez-tez uchraydigan xatolar
credentials: true bilan origin: '*' birga ishlamaydi
// ❌ Brauzer ruxsat bermaydi
app.enableCors({
origin: '*', // barcha domenlar
credentials: true, // cookie — birga ishlamaydi
});
// ✅ Aniq domen ko'rsatilishi kerak
app.enableCors({
origin: 'https://myapp.uz',
credentials: true,
});
Helmet Content-Security-Policy inline skriptlarni bloklaydi
// ❌ Brauzerda inline <script> yoki eval ishlaydi — CSP bloklab qo'yadi
// Natija: sahifa buzilgan ko'rinadi
// ✅ Dev da CSP ni o'chirish
app.use(helmet({ contentSecurityPolicy: false }));
// Yoki nonce bilan — ilg'or mavzu
OPTIONS so'rovga javob yo'q
Brauzer preflight: OPTIONS /api/users → Server: 404
Sabab: OPTIONS method route da yo'q
✅ Yechim: CORS options ichida 'OPTIONS' qo'shish yoki:
app.enableCors() — OPTIONS ni avtomatik boshqaradi
Xulosa
Helmet — xavfsizlik headerlarini bir qatorda. CORS — qaysi frontendlarga ruxsat berishni belgilash.
Eslab qoling:
app.use(helmet())— standart xavfsizlik headerlariapp.enableCors({ origin, credentials })— CORS sozlashcredentials: truebilanorigin: '*'birga ishlamaydi- Dev da soddaroq, prod da qattiqroq sozlash
- ALLOWED_ORIGINS whitelist — noaniq domen bloklanadi
Keyingi dars
35-dars: Compression — gzip, brotli