Набор defense-in-depth мелочей из ревью безопасности. Ни одна не критична сама по себе, но стоит закрыть пачкой.
Сессионный cookie Secure завязан на NODE_ENV
src/server.ts:180 → middleware/session.ts:34: secure: env.NODE_ENV === 'production', а NODE_ENV по умолчанию development (env.ts:5). При деплое без NODE_ENV=production auth-cookie ходит по plaintext HTTP.
Фикс: отдельный флаг COOKIE_SECURE (default on, отключается явно), а не косвенно через NODE_ENV.
/metrics без аутентификации, когда METRICS_TOKEN не задан
server.ts:186, routes/metrics.ts:18. Сравнение токена constant-time и корректно, но без токена метрики (объёмы отправки, счётчики очереди/вебхуков) публичны.
Фикс: fail-closed (404/403 без токена) либо явно задокументировать «должно быть network-restricted».
Нет проверки Origin/Referer на admin-мутациях
SameSite=lax (middleware/session.ts:37) закрывает CSRF на cross-site non-GET, отдельного слоя нет.
Фикс: добавить проверку Origin для мутаций /admin/api как второй слой.
SSRF-проверка вебхуков подвержена DNS-rebinding TOCTOU
src/lib/ssrf.ts:143 assertPublicUrl резолвит хост, затем fetch() (webhooks/service.ts:182) резолвит независимо — второй ответ может быть приватным IP/169.254.169.254. Уже смягчено: redirect: 'manual', protocol allow-list, богатый блок-лист, создание вебхуков admin-only, WEBHOOK_ALLOW_PRIVATE=false.
Фикс (харднинг): пиновать провалидированный IP на само соединение (кастомный lookup/agent).
OIDC nonce генерируется, но не проверяется
routes/auth.ts:67 кладёт nonce, oidc-provider.ts его не читает. По дизайну корректно (identity берётся прямым server-to-server userinfo, CSRF закрыт проверенным state).
Действие: удалить мёртвое поле для ясности.
Набор defense-in-depth мелочей из ревью безопасности. Ни одна не критична сама по себе, но стоит закрыть пачкой.
Сессионный cookie
Secureзавязан наNODE_ENVsrc/server.ts:180→middleware/session.ts:34:secure: env.NODE_ENV === 'production', аNODE_ENVпо умолчаниюdevelopment(env.ts:5). При деплое безNODE_ENV=productionauth-cookie ходит по plaintext HTTP.Фикс: отдельный флаг
COOKIE_SECURE(default on, отключается явно), а не косвенно черезNODE_ENV./metricsбез аутентификации, когдаMETRICS_TOKENне заданserver.ts:186,routes/metrics.ts:18. Сравнение токена constant-time и корректно, но без токена метрики (объёмы отправки, счётчики очереди/вебхуков) публичны.Фикс: fail-closed (404/403 без токена) либо явно задокументировать «должно быть network-restricted».
Нет проверки Origin/Referer на admin-мутациях
SameSite=lax(middleware/session.ts:37) закрывает CSRF на cross-site non-GET, отдельного слоя нет.Фикс: добавить проверку
Originдля мутаций/admin/apiкак второй слой.SSRF-проверка вебхуков подвержена DNS-rebinding TOCTOU
src/lib/ssrf.ts:143assertPublicUrlрезолвит хост, затемfetch()(webhooks/service.ts:182) резолвит независимо — второй ответ может быть приватным IP/169.254.169.254. Уже смягчено:redirect: 'manual', protocol allow-list, богатый блок-лист, создание вебхуков admin-only,WEBHOOK_ALLOW_PRIVATE=false.Фикс (харднинг): пиновать провалидированный IP на само соединение (кастомный
lookup/agent).OIDC
nonceгенерируется, но не проверяетсяroutes/auth.ts:67кладётnonce,oidc-provider.tsего не читает. По дизайну корректно (identity берётся прямым server-to-serveruserinfo, CSRF закрыт провереннымstate).Действие: удалить мёртвое поле для ясности.