feat(security): проверка Origin, строгий CSP и лимиты загрузок
- middleware OriginCheck: state-changing запросы с чужого Origin отклоняются (AGENT.md 9.7); запросы без Origin пропускаются — cookie уже SameSite=Lax - CSP перечисляет директивы явно (script-src/worker-src/manifest-src 'self', style-src с inline для React, img-src с внешними https для аватаров вебхуков, connect-src с доменом файлов и LiveKit); добавлены Permissions-Policy, Cross-Origin-Opener-Policy и X-Permitted-Cross-Domain-Policies - лимиты по AGENT.md 8.6: загрузки 10/мин и 100/сутки (вложения и аватары), реакции 20/мин; администратор инстанса лимиты обходит - step-up: смена прав роли (в теле PATCH) и удаление сервера (перед вызовом /auth/step-up) требуют свежего подтверждения личности - тесты: Origin (свой/чужой/GET), состав CSP, лимит реакций, step-up на права роли; исправлен вызов NewRateLimiter (второй аргумент — burst, не окно)
This commit is contained in:
@@ -61,6 +61,12 @@ type Server struct {
|
||||
inviteLimiter *httpx.RateLimiter
|
||||
// webhookLimiter — 30 сообщений в минуту на вебхук (AGENT.md 7.11, 8.6).
|
||||
webhookLimiter *httpx.RateLimiter
|
||||
// uploadLimiter и uploadDailyLimiter — 10 загрузок в минуту и 100 в сутки
|
||||
// на пользователя (AGENT.md 8.6).
|
||||
uploadLimiter *httpx.RateLimiter
|
||||
uploadDailyLimiter *httpx.RateLimiter
|
||||
// reactionLimiter — 20 реакций в минуту на пользователя.
|
||||
reactionLimiter *httpx.RateLimiter
|
||||
// slowmode — время последней отправки в комнату для режима медленной
|
||||
// отправки; словарь ограничен по размеру (AGENT.md 7.5).
|
||||
slowmodeMu sync.Mutex
|
||||
@@ -111,9 +117,13 @@ func New(cfg config.Config, db *database.DB, logger *slog.Logger, deps Deps) *Se
|
||||
soundboardLimiter: httpx.NewRateLimiterWindow(3, 10*time.Second, 3),
|
||||
inviteLimiter: httpx.NewRateLimiterWindow(10, 24*time.Hour, 10),
|
||||
webhookLimiter: httpx.NewRateLimiterWindow(webhookRateLimit, time.Minute, webhookRateLimit),
|
||||
slowmode: map[string]time.Time{},
|
||||
presence: map[uint64]time.Time{},
|
||||
webhookSeen: map[string]time.Time{},
|
||||
// Второй аргумент NewRateLimiter — запас (burst), а не окно.
|
||||
uploadLimiter: httpx.NewRateLimiter(10, 10),
|
||||
uploadDailyLimiter: httpx.NewRateLimiterWindow(100, 24*time.Hour, 100),
|
||||
reactionLimiter: httpx.NewRateLimiter(20, 20),
|
||||
slowmode: map[string]time.Time{},
|
||||
presence: map[uint64]time.Time{},
|
||||
webhookSeen: map[string]time.Time{},
|
||||
// Дашборд опрашивает метрики раз в секунду: 120/мин с запасом.
|
||||
metricsLimiter: httpx.NewRateLimiter(120, 30),
|
||||
sysinfoPaths: sysinfo.DefaultPaths(),
|
||||
@@ -135,6 +145,8 @@ func New(cfg config.Config, db *database.DB, logger *slog.Logger, deps Deps) *Se
|
||||
apiRouter.Use(s.sessionContext)
|
||||
// Метрики дашборда вне общего лимита: у них свой (см. api_metrics.go).
|
||||
apiRouter.Use(s.apiLimiter.MiddlewareExcept(apiRateLimitKey, metricsPath))
|
||||
// Строгая проверка Origin для state-changing запросов (AGENT.md 9.7).
|
||||
apiRouter.Use(httpx.OriginCheck(cfg.AllowedOrigins()))
|
||||
s.api = s.registerAPI(apiRouter)
|
||||
s.registerMetaRoutes(s.api)
|
||||
s.registerAuthRoutes(apiRouter)
|
||||
@@ -163,7 +175,10 @@ func New(cfg config.Config, db *database.DB, logger *slog.Logger, deps Deps) *Se
|
||||
s.registerRoutes(router)
|
||||
|
||||
handler := httpx.Chain(router,
|
||||
httpx.SecurityHeaders(cfg.FilesDomain),
|
||||
httpx.SecurityHeaders(httpx.SecurityOptions{
|
||||
FilesDomain: cfg.FilesDomain,
|
||||
LiveKitURL: cfg.LiveKitURL,
|
||||
}),
|
||||
httpx.RequestID,
|
||||
httpx.RequestInfoMiddleware,
|
||||
httpx.Logger(logger),
|
||||
|
||||
Reference in New Issue
Block a user