This guide documents the current security posture of Clothica Shop Backend and the operational practices expected for production deployments.
- Protect customer accounts and admin access.
- Avoid raw bearer token storage.
- Fail fast on unsafe production configuration.
- Reduce brute force, CSRF, CORS and upload abuse risk.
- Keep dependency and logging exposure under control.
Authentication uses opaque cookies, not JWTs.
Client cookies:
accessTokenrefreshTokensessionId
Database storage:
accessTokenHash: HMAC-SHA256 hash of the access tokenrefreshTokenHash: HMAC-SHA256 hash of the refresh token- expiration timestamps for access/refresh lifetimes
The raw token values are only sent to the browser as HTTP-only cookies. A database leak should not directly expose reusable access or refresh tokens without SESSION_TOKEN_SECRET.
Session cookies are:
httpOnly: truesameSite: controlled byCOOKIE_SAME_SITE, defaults tononein production andlaxin developmentsecure: enabled in productionpriority: high
Production deployments must run behind HTTPS. If SameSite=None is used, secure cookies are mandatory.
Mutating requests are checked with Origin/Referer validation:
- Safe methods
GET,HEAD,OPTIONSare not checked. - State-changing requests require an allowed origin in strict production mode.
- Same-origin admin requests are accepted.
- Telegram webhook route is excluded and protected separately.
Configuration:
| Variable | Values | Default |
|---|---|---|
CSRF_ORIGIN_CHECK |
strict, session, off |
strict in production, session in development |
Do not set CSRF_ORIGIN_CHECK=off in production unless another trusted gateway provides equivalent protection.
Production CORS is allowlist-based. Configure trusted origins with:
CLIENT_URLCLIENT_URL_2CLIENT_URL_LOCALCLIENT_URLSAPI_URLBACKEND_URLRENDER_EXTERNAL_URL
CLIENT_URLS accepts a comma-separated list.
Requests without an Origin header are allowed to support non-browser clients, server-to-server requests and health checks.
- Passwords are hashed with bcrypt.
- Default cost is
12. - Allowed production range is controlled by
BCRYPT_SALT_ROUNDS, clamped to10-14. - New registration/reset passwords require 10-128 characters.
- Existing lower-cost bcrypt hashes are rehashed on successful login.
Password reset uses Telegram-linked accounts:
- Reset codes are 6-digit numeric codes.
- Codes are sent through Telegram only when the account is linked.
- Codes are stored as HMAC hashes.
- Codes expire after 10 minutes.
- Failed attempts are counted and locked after 5 invalid attempts.
- Existing sessions are deleted after a successful password reset.
The response to password reset requests does not reveal whether the phone number exists or whether Telegram is linked.
Telegram has two separate security-sensitive flows.
The profile endpoint generates a short-lived random link token:
https://t.me/<bot>?start=<random-token>
Only an HMAC hash of that token is stored. The token expires after 10 minutes and is cleared after successful linking.
The webhook endpoint is:
/api/telegram/webhook/:webhookSecret
Verification layers:
- constant-time comparison of
:webhookSecret - optional separate
TELEGRAM_WEBHOOK_PATH_SECRET - required production
TELEGRAM_WEBHOOK_SECRET - Telegram header
X-Telegram-Bot-Api-Secret-Token
Webhook URLs are not logged.
AdminJS is mounted at /admin.
Controls:
- Authenticated router.
- Only users with
role: "admin"can sign in. - Mongo-backed sessions via
connect-mongo. saveUninitialized: false.resave: false.- Secure HTTP-only cookies.
- Admin login attempt rate limiting.
- Admin password edits are hashed server-side before save.
- Password reset token fields are hidden from AdminJS views.
| Limiter | Scope | Current default |
|---|---|---|
| API limiter | /api |
300 requests / 15 minutes |
| Auth limiter | register/login by IP + phone | 10 attempts / 15 minutes |
| Password reset limiter | reset request/submit by IP + phone | 5 attempts / 15 minutes |
| Public write limiter | orders/feedbacks/subscriptions | 60 submissions / 15 minutes |
| Admin login limiter | POST /admin/login |
20 attempts / 15 minutes |
| Search limiter | product search requests | 30 search requests / minute |
Uploads use Multer memory storage with:
- 2 MB file size limit
- MIME whitelist:
image/jpeg,image/png,image/webp,image/gif - extension whitelist:
.jpg,.jpeg,.png,.webp,.gif - Cloudinary image upload as the final storage layer
Do not expose generic file upload endpoints without adding equivalent validation.
The HTTP logger redacts:
AuthorizationCookieSet-CookieX-Telegram-Bot-Api-Secret-Token
Telegram webhook requests are excluded from automatic request logging to avoid leaking secret URL segments.
Both package roots use npm overrides for patched transitive dependencies that are pulled through AdminJS or validation packages:
@tiptap/extension-linki18next-http-backendlodashtinymceuuid
Run:
npm run audit:security
cd clothica-shop
npm audit --audit-level=highBoth audits should report found 0 vulnerabilities.
When NODE_ENV=production, startup fails if:
MONGO_URLis missing.ADMIN_SESSION_SECRETis missing or weak.ADMIN_COOKIE_SECRETis missing or weak.SESSION_TOKEN_SECRETis missing or weak.- no trusted origin is configured.
- Telegram webhook is enabled without
RENDER_EXTERNAL_URL. - Telegram webhook is enabled without
TELEGRAM_WEBHOOK_SECRET.
Secrets are considered weak if they are shorter than 32 characters or contain placeholder-like values such as secret, password, changeme or change-me.
Recommended rotation plan:
- Rotate
ADMIN_SESSION_SECRETandADMIN_COOKIE_SECRETto invalidate AdminJS sessions. - Rotate
SESSION_TOKEN_SECRETto invalidate all API sessions, password reset codes and Telegram link tokens. - Rotate
TELEGRAM_WEBHOOK_SECRETin the deployment and re-register the webhook. - Rotate provider credentials in Cloudinary, Resend and Telegram if those systems may be affected.
- Ask users to log in again after session secret rotation.
NODE_ENV=production- HTTPS enabled at the platform/load balancer.
- Strong secrets generated with a cryptographic random source.
- Correct CORS origins configured.
COOKIE_SAME_SITEmatches frontend/backend hosting topology.TELEGRAM_WEBHOOK_SECRETconfigured when Telegram is enabled.- Swagger disabled unless intentionally exposed.
npm audit --audit-level=highis clean in both package roots.- Admin user created with a strong password.
- Database backups and restore process are tested.