Skip to content

Security

можно. implements multiple layers of protection: JWT authentication for the web dashboard, API keys for SDKs, rate limiting to prevent attacks, and security headers for browser protection.

Authentication

JWT (Web Dashboard and REST API)

Algorithm: HMAC-SHA256. Token structure:

json
{
  "sub": "1",
  "email": "admin@example.com",
  "role": "ADMIN",
  "iss": "mozhno",
  "iat": 1719000000,
  "exp": 1719000900
}
ParameterDefaultDescription
Access token TTL15 minutesJWT_ACCESS_TOKEN_TTL_MINUTES
Refresh token TTL30 daysJWT_REFRESH_TOKEN_TTL_DAYS
SecretRequired in productionJWT_SECRET, minimum 256 bits
IssuermozhnoJWT_ISSUER

Refresh Token Rotation (Family Rotation)

When refreshing an access token:

  1. The old refresh token is invalidated
  2. A new refresh token is generated in the same "family"
  3. If a stolen token is reused — the entire family is revoked
sequenceDiagram
    participant Client
    participant Server

    Client->>Server: POST /api/v1/auth/refresh { refreshToken: "A1" }
    Server->>Server: Look up A1 in family α
    Server->>Server: Invalidate A1
    Server->>Server: Generate A2 (family α)
    Server-->>Client: { token, refreshToken: "A2" }

    Note over Client: Attacker uses A1

    Client->>Server: POST /api/v1/auth/refresh { refreshToken: "A1" }
    Server->>Server: A1 found but already used!
    Server->>Server: Revoke ENTIRE family α
    Server-->>Client: 401 TOKEN_REUSE

This means: if a refresh token is compromised, the attacker gets at most one session, after which the legitimate user is forced to re-login.

API Keys (SDK)

The key is passed as a Bearer token: Authorization: Bearer <api-key>. The key type determines allowed operations:

TypeAccess
SERVERGET /api/client/features, POST /api/client/metrics
FRONTENDPOST /api/client/evaluate, POST /api/client/metrics

See API Keys for details.

BCrypt

Passwords are hashed with BCrypt at strength 12. This means ~0.3 seconds per password check — slow enough to resist brute-force, fast enough for comfortable login.

Rate Limiting

Uses the Token Bucket algorithm (Bucket4j). Limits are applied to the client's IP address:

EndpointCapacityRefillInterval
POST /api/v1/auth/login5+51 min
POST /api/v1/auth/forgot-password, /reset-password, /accept-invite3+360 min
POST /api/v1/auth/refresh10+101 min
POST /api/client/* (SDK)1000+10001 min
POST/PUT/DELETE /api/v1/* (admin write)100+1001 min

Exceeding the limit returns 429 RATE_LIMIT_EXCEEDED.

All limits are configurable via environment variables — see Configuration.

Browser Protection

CORS

Configured via APP_CORS_ALLOWED_ORIGINS. In production, specify a concrete domain:

APP_CORS_ALLOWED_ORIGINS=https://app.example.com

Security Headers

HeaderValue
Strict-Transport-Securitymax-age=31536000 (1 year)
Content-Security-PolicyConfigured to prevent XSS

Production Recommendations

RecommendationHow To
Strong JWT_SECRETopenssl rand -base64 32
HTTPS-onlyUse a reverse proxy (Nginx, Traefik, Caddy) with TLS
Restricted CORSSpecify a concrete domain, not *
Secrets managerDon't store passwords and keys in docker-compose.yml
Key rotationAPI keys — quarterly; JWT_SECRET — when team changes
Don't run as rootDocker: user: '1000:1000', read-only FS
Logging without secretsSensitiveDataMasker masks JWT, keys, passwords in logs

Released under the AGPL v3.0 License.