Преминете към основното съдържание

Сигурност

Тази страница описва мерките за сигурност, прилагани в Dictaro на всички нива -- автентикация, авторизация, транспорт, rate limiting и управление на секрети.

JWT RS256 жизнен цикъл

Dictaro използва RS256 (RSA-SHA256) JWT токени за автентикация между клиента и licensing сървъра.

Създаване на токен (сървър)

JWT токенът се създава от Go сървъра чрез services.CreateAccessToken():

Алгоритъм:   RS256 (RSA + SHA-256)
Подписване: RSA Private Key (2048-bit, PEM PKCS1/PKCS8)
Верификация: RSA Public Key (PEM PKIX)

JWT Claims структура:

ClaimТипОписание
substringEmail на потребителя
user_idstring (UUID)Уникален идентификатор на потребителя
planstring"free" или "pro"
device_idstringSHA-256 на machine ID (ако е предоставен)
providerstringOAuth provider (google, azure, github, email)
iatint64Issued At (Unix timestamp)
expint64Expiry (Unix timestamp, iat + JWT_EXPIRY_DAYS * 86400)

Конфигурация:

  • JWT_EXPIRY_DAYS: 7 дни (по подразбиране)
  • Сървърен leeway: 5 секунди (за clock skew)
  • Клиентски leeway: 60 секунди

Валидация на токен (клиент)

Клиентът валидира JWT токени локално с вграден RS256 public key:

  1. Декодира JWT header, проверява alg == RS256
  2. Верифицира подписа с embedded PEM public key
  3. Проверява exp claim с 60-секунден leeway
  4. Извлича AccountInfo от claims (email, plan, provider)

Периодичен refresh

Съхранение на токени

ПлатформаStorage механизъмДостъп
WindowsWindows Credential Managerkeyring crate, service=dictaro, user=jwt_token
macOSmacOS Keychainkeyring crate с apple-native feature
LinuxSecret Service API (DBus)keyring crate (fallback)

Токените никога не се записват на файловата система, а се съхраняват в защитеното хранилище на ОС.

OAuth2 потоци

Поддържани провайдъри

OAuth конфигурация

ПровайдърAuth URLToken URLScopesUserInfo endpoint
Googleaccounts.google.com/o/oauth2/v2/authoauth2.googleapis.com/tokenopenid email profilegoogleapis.com/oauth2/v3/userinfo
Azure ADlogin.microsoftonline.com/{tenant}/oauth2/v2.0/authorizelogin.microsoftonline.com/{tenant}/oauth2/v2.0/tokenopenid email profile User.Readgraph.microsoft.com/v1.0/me
GitHubgithub.com/login/oauth/authorizegithub.com/login/oauth/access_tokenuser:emailapi.github.com/user + /user/emails

CSRF защита

OAuth flow-ът използва двуслойна state защита:

  1. Клиентски state -- SHA-256 на (timestamp + PID), верифициран при callback на localhost
  2. Сървърен nonce -- 32-byte криптографски random hex, записан в session cookie, верифициран при OAuth callback

Session cookies

Session cookies се управляват чрез gorilla/securecookie:

  • HttpOnly: Да (не се достъпва от JavaScript)
  • Secure: Да, ако BASE_URL е HTTPS
  • SameSite: Lax (по подразбиране на Gin)
  • Съдържание: {oauth_nonce, redirect_uri, app_state, device_id}
  • Lifetime: Сесийна (изтрива се при затваряне на браузъра)

Email/Password автентикация

В допълнение към OAuth, Dictaro поддържа email/password регистрация:

  • Password hashing: bcrypt (golang.org/x/crypto/bcrypt)
  • Email верификация: Задължителна преди вход; изпраща се чрез Resend API
  • Password reset: Token-based flow с expiry, изпратен по email
  • Turnstile защита: Login страницата включва Cloudflare Turnstile challenge

API Key автентикация за ASR

ASR сървърът използва споделен API key за автентикация:

ПараметърОписание
SERVER_API_KEYСподелен ключ, зададен като env variable
ТранспортИзпраща се в JSON metadata при WebSocket connect
ВалидацияСървърът проверява ключа преди обработка на аудио
{
"type": "transcribe",
"api_key": "shared-secret-key",
"language": "bg",
"task": "transcribe",
"machine_id": "device-hash",
"postprocess": true
}

API ключът е вграден в клиента и осигурява базова защита срещу неоторизиран достъп до ASR ресурса.

Cloudflare Turnstile

Bot protection на login страницата:

Конфигурация:

  • TURNSTILE_SITE_KEY -- публичен ключ за frontend widget
  • TURNSTILE_SECRET_KEY -- секретен ключ за server-side валидация

Rate Limiting

Per-IP rate limiting с token bucket алгоритъм (golang.org/x/time/rate):

Rate limit конфигурация

Endpoint категорияRateBurstMax IPsEndpoints
Login5/мин510,000/auth/login/:provider, /auth/register, /auth/login/email
Sensitive3/мин310,000/auth/confirm-email, /auth/resend-verification, /auth/forgot-password, /auth/reset-password
Refresh30/мин3010,000/auth/refresh

Имплементация

Защити срещу memory exhaustion:

  • Maximum 10,000 tracked IP адреса на limiter
  • Автоматично почистване на stale entries (> 10 мин неактивност)
  • При достигане на maxSize, нови IP адреси получават temporary limiter (без tracking)

CORS конфигурация

CORS middleware прилага whitelist подход -- само изрично разрешени origins получават CORS headers:

# .env конфигурация
CORS_ORIGINS=["http://localhost:3000","http://localhost:9876","https://dictaro.ai"]
HeaderСтойност
Access-Control-Allow-OriginКонкретният origin от whitelist-a
Access-Control-Allow-Credentialstrue
Access-Control-Allow-MethodsGET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-HeadersContent-Type, Authorization
Access-Control-Max-Age3600 (1 час preflight cache)

Fail-closed поведение: Ако CORS_ORIGINS не е конфигуриран (празен масив), никакви CORS headers не се добавят -- кросс-origin заявки са напълно блокирани.

Preflight OPTIONS заявки получават 204 No Content без обработка от route handler-ите.

Security Headers

Всички HTTP отговори от licensing API-то включват следните security headers:

HeaderСтойностПредназначение
X-Content-Type-OptionsnosniffПредотвратява MIME type sniffing
X-Frame-OptionsDENYБлокира iframe embedding (clickjacking protection)
Referrer-Policystrict-origin-when-cross-originОграничава Referer header при cross-origin навигация
Strict-Transport-Securitymax-age=31536000; includeSubDomainsHSTS -- принуждава HTTPS за 1 година (само при HTTPS BASE_URL)

Redirect URI валидация

OAuth callback redirect URI-та се валидират срещу конфигуриран whitelist:

# .env конфигурация
ALLOWED_REDIRECT_URIS=["http://localhost","https://app.dictaro.ai"]

Правила за валидация:

  1. URI трябва да започва с http:// или https://
  2. Scheme трябва да съвпада с prefix-а
  3. Host се сравнява точно (не prefix match) -- предотвратява spoofing като https://app.dictaro.ai.evil.com
  4. Ако prefix-ът указва порт, той трябва да съвпада
  5. Ако prefix-ът указва path, URI path-ът трябва да започва с него
  6. В development mode (празен whitelist) -- всички http:// и https:// URI-та са разрешени

Управление на секрети

Среди и методи

Списък на секретите

СекретСредаStorageПредназначение
DATABASE_URLProduction.env + GitHub SecretsPostgreSQL connection string
JWT_PRIVATE_KEYProductionGitHub Secrets → deployRS256 подписване на JWT токени
JWT_PUBLIC_KEYProduction + ClientGitHub Secrets + embeddedRS256 валидация на JWT токени
SESSION_SECRETProduction.envПодписване на session cookies (min 32 chars)
GOOGLE_CLIENT_ID/SECRETProduction.env + GitHub SecretsGoogle OAuth2
AZURE_CLIENT_ID/SECRETProduction.env + GitHub SecretsAzure AD OAuth2
GITHUB_CLIENT_ID/SECRETProduction.env + GitHub SecretsGitHub OAuth2
STRIPE_SECRET_KEYProduction.env + GitHub SecretsStripe API достъп
STRIPE_WEBHOOK_SECRETProduction.env + GitHub SecretsВалидация на Stripe webhook signatures
RESEND_API_KEYProduction.env + GitHub SecretsИзпращане на email-и
TURNSTILE_SITE_KEYProduction.envCloudflare Turnstile (публичен)
TURNSTILE_SECRET_KEYProduction.envCloudflare Turnstile (сървърен)
SERVER_API_KEYASR Production.envАвтентикация на ASR заявки
CLOUDFLARE_TUNNEL_TOKENASR Production.envCloudflare Tunnel за ASR
CLOUDFLARE_TUNNEL_TOKEN_MONITORINGProduction.envCloudflare Tunnel за Grafana
CLOUDFLARE_TUNNEL_TOKEN_LOKIProduction.envCloudflare Tunnel за Loki
HF_TOKENASR Production.envHugging Face token за модели
GRAFANA_PASSWORDProduction.envGrafana admin парола
GRAFANA_AZUREAD_CLIENT_ID/SECRETProduction.envAzure AD SSO за Grafana

Gitignored файлове

Следните файлове и директории са добавени в .gitignore:

  • *.env, .env.* -- всички environment файлове
  • licensing-go/keys/ -- RSA ключове за JWT
  • *.pem -- PEM ключови файлове

Stripe Webhook сигурност

Stripe webhook-ове се валидират чрез HMAC подпис:

  1. Stripe подписва payload с STRIPE_WEBHOOK_SECRET
  2. Сървърът извлича Stripe-Signature header
  3. webhook.ConstructEvent() верифицира подписа преди обработка
  4. Body size е ограничен до 1 MB за защита срещу abuse
event, err := webhook.ConstructEvent(payload, sigHeader, h.cfg.StripeWebhookSecret)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": "Invalid Stripe signature"})
return
}

Device Account Limiting

За предотвратяване на злоупотреби с безплатни акаунти, системата ограничава броя на акаунтите от едно устройство:

ПараметърСтойностОписание
DEVICE_ACCOUNTS_MAX3Максимален брой различни акаунти от едно устройство
COOLDOWN_HOURS5Период на блокиране при превишаване
  • Device ID се изчислява като SHA-256 на machine-specific идентификатори
  • Само за free акаунти -- Pro потребителите не подлежат на device limiting
  • При достигане на лимита, сървърът връща error=device_limit с retry_at timestamp

Обобщение на сигурностните слоеве