security: fix 7 vulnerabilities from Opus code review
- [A01] /export: mount after auth.middleware, filter by company for non-admin - [A02] OIDC: verify issuer, audience, select key by kid; preload JWKS at startup - [A07] session fixation: session.regenerate() in POST /login, GET /callback, POST /dev-login - [A10] open redirect: safeReturn() helper validates returnTo (blocks //evil.com, https:// etc) - [A05] CSRF: getSessionIdentifier uses req.sessionID instead of dead req.cookies.jwt - [A05] fail-fast: reject default SESSION_SECRET / CSRF_SECRET in NODE_ENV=production - [A05] DEV_MODE=true blocked in production Tests: 68 PASS, 0 FAIL (was 50, added 18 new security-focused tests)
This commit is contained in:
@@ -75,20 +75,28 @@ async function start() {
|
||||
|
||||
// ── Публичные маршруты (до auth.middleware) ──────────────────────────────────
|
||||
|
||||
// ── Fail-fast в production ────────────────────────────────────────────────
|
||||
// Если секреты не заданы — приложение в проде немедленно падает.
|
||||
// Дефолтные значения — только для локальной разработки.
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const DEFAULT_SESSION = 'dev-session-secret-change-me';
|
||||
const DEFAULT_CSRF = 'dev-csrf-secret-change-in-prod';
|
||||
if (!process.env.SESSION_SECRET || process.env.SESSION_SECRET === DEFAULT_SESSION) {
|
||||
console.error('FATAL: SESSION_SECRET не задан или равен дефолту в production!');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!process.env.CSRF_SECRET || process.env.CSRF_SECRET === DEFAULT_CSRF) {
|
||||
console.error('FATAL: CSRF_SECRET не задан или равен дефолту в production!');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// k8s liveness probe — простая проверка что сервер жив
|
||||
app.get('/healthz', (req, res) => res.send('OK'));
|
||||
|
||||
// JWKS endpoint для валидации токенов внешними сервисами (только в mock-режиме)
|
||||
if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler);
|
||||
|
||||
// GET /export: агрегированный список CIDR, без авторизации.
|
||||
// src/routes/export.js содержит логику и комментарии.
|
||||
app.use(require('./src/routes/export').createRouter({
|
||||
q,
|
||||
exportLimiter,
|
||||
aggregateCIDRs,
|
||||
}));
|
||||
|
||||
// GET /login, POST /login, GET /logout.
|
||||
// src/routes/auth.js содержит логику и комментарии.
|
||||
app.use(require('./src/routes/auth').createRouter({
|
||||
@@ -98,9 +106,18 @@ async function start() {
|
||||
MOCK_USERS,
|
||||
}));
|
||||
|
||||
// ── Все маршруты ниже требуют валидного JWT (проверяет auth.middleware) ───────
|
||||
// ── Все маршруты ниже требуют авторизации (auth.middleware) ────────────────
|
||||
app.use(auth.middleware);
|
||||
|
||||
// GET /export: CIDR текущего пользователя (или всех компаний для admin).
|
||||
// Перемещён за auth.middleware — изоляция данных по компании.
|
||||
// src/routes/export.js содержит логику и комментарии.
|
||||
app.use(require('./src/routes/export').createRouter({
|
||||
q,
|
||||
exportLimiter,
|
||||
aggregateCIDRs,
|
||||
}));
|
||||
|
||||
// GET /, POST /add, POST /edit/:id, POST /delete/:id.
|
||||
// src/routes/entries.js содержит логику и комментарии.
|
||||
app.use(require('./src/routes/entries').createRouter({
|
||||
|
||||
Reference in New Issue
Block a user