Skip to content

Безопасность

SLOzy реализует многоуровневую систему безопасности: JWT-аутентификация, CORS, rate limiting и защитные заголовки.

JWT-аутентификация

Аутентификация построена на JSON Web Token. Access-токен живёт 8 часов (настраивается через JWT_EXPIRATION), refresh-токен — 7 дней.

go
JWTSecret:     getEnv("JWT_SECRET", "dev_change_me_in_production_min_32_chars"),
JWTExpiration: getEnv("JWT_EXPIRATION", "8h"),

Токены подписываются HMAC-SHA256. Refresh-токены хранятся в таблице user_sessions и позволяют безопасно продлевать сессию без повторного ввода пароля.

Эндпоинты аутентификации

МетодПутьОписание
POST/api/v1/auth/registerРегистрация, возвращает access + refresh токены
POST/api/v1/auth/loginВход, возвращает токены
POST/api/v1/auth/refreshОбновление токенов
POST/api/v1/auth/logoutИнвалидация сессии на сервере
GET/api/v1/auth/meИнформация о текущем пользователе

CORS

CORS-мидлвар проверяет заголовок Origin и сравнивает со списком разрешённых источников (CORS_ORIGINS).

go
func CORSMiddleware(allowedOrigins []string) gin.HandlerFunc {
    return func(c *gin.Context) {
        origin := c.Request.Header.Get("Origin")
        // проверка по белому списку
        if allowed {
            c.Header("Access-Control-Allow-Origin", origin)
        }
        c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
        c.Header("Access-Control-Allow-Credentials", "true")
        c.Header("Access-Control-Max-Age", "86400")
    }
}

По умолчанию разрешены http://localhost:3000 и http://localhost:8080. Для production указывается домен приложения: https://slozy.net.

Rate Limiting

Реализован алгоритм Token Bucket. Два уровня ограничений:

Глобальный лимит

ПараметрЗначение
Лимит1000 запросов в минуту
Burst50 запросов

Лимит на IP

ПараметрЗначение
Лимит100 запросов в минуту
Burst10 запросов

При превышении возвращается 429 Too Many Requests:

json
{
  "error": "Rate limit exceeded",
  "limit": 100,
  "window": "1 minute",
  "message": "Too many requests from your IP address"
}

В ответ добавляются заголовки: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.

IP-вайтлист и блэк-лист

IP-адреса из IP_WHITELIST пропускаются без проверки лимитов. Адреса из блэк-листа блокируются с 403 Forbidden.

Security Headers

Минимальный набор заголовков, устанавливаемых на каждый ответ:

ЗаголовокЗначениеНазначение
X-Frame-OptionsDENYЗащита от clickjacking
X-Content-Type-OptionsnosniffЗапрет MIME-sniffing
X-XSS-Protection1; mode=blockЗащита от XSS (устаревший, но для совместимости)
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preloadHSTS — принудительный HTTPS
Content-Security-Policydefault-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' wss://localhost:*CSP — контроль источников контента
Referrer-Policystrict-origin-when-cross-originКонтроль Referer-заголовка
Permissions-Policydefault-src 'self'Ограничение API браузера
Cross-Origin-Opener-Policysame-originИзоляция COOP
Cross-Origin-Embedder-Policyrequire-corpИзоляция COEP
ServerSLOzyМаскировка серверного ПО

Request Size Limiter

Размер тела запроса ограничен 1 МБ. При превышении возвращается 413 Request Entity Too Large.

Логирование попыток входа

Неудачные попытки входа логируются в таблицу login_attempts:

sql
INSERT INTO login_attempts (user_id, ip_address, user_agent, success, failure_reason, created_at)
VALUES ($1, $2, $3, $4, $5, NOW())

Настройка безопасности

env
ENABLE_SECURITY=true
ENABLE_RATE_LIMIT=true
GLOBAL_RATE_LIMIT=1000
PER_IP_RATE_LIMIT=100
ENABLE_IP_WHITELIST=false
IP_WHITELIST=127.0.0.1,10.0.0.0/8
ALLOWED_ORIGINS=http://localhost:3000,https://slozy.net