Skip to content

Security

TelegramOS security: tenant isolation, authentication, encryption at rest, audit trail and outbound-request controls.

The platform implements security controls at multiple layers to protect tenant data, prevent unauthorized access, and ensure safe operation. This page describes what is actually enforced in the running code.

Tenant isolation

LayerMechanismWhat it prevents
Tenant boundaryEvery resource row (account, proxy, bot, conversation, wallet) carries the owning user's ID. Read and write queries are scoped to the caller's own tenant plus any team tenants that grant the specific permission (for example inbox:read, accounts:delete); on a lookup failure the scope falls back to owner-only. A foreign or malformed ID returns 404, not 403.One tenant reading or changing another tenant's data through the API or UI.
Per-account runtimeEach active account runs as its own child process inside the worker manager, with its own decrypted session, its own MTProto connection, its own device fingerprint and its own proxy. No connection is multiplexed across accounts. All workers share the worker-manager container's CPU and memory limits; there is no per-process sandbox.One account's Telegram session being observed or reused by another account's worker.
Per-account proxyAn account is bound to exactly one proxy; assigning several accounts to the same proxy is refused. Platform-rented proxies are provisioned as a per-account provider sub-user and torn down when the account is released. Rotation is per account.Telegram linking accounts through a shared exit IP.
Rented-account recyclingA tenant only receives an account that is currently unallocated in the pool. When a rental ends (subscription or bundle expiry plus a 5-day grace period, or an explicit return), the reclamation worker deletes every chat on both sides, clears the tenant binding and returns the account to the pool with its session intact, so a later tenant can receive it. Accounts whose session died are quarantined and never re-sold. A paid permanent delete ($1) logs the session out, wipes the encrypted session blob and retires the account for good.A new tenant inheriting a previous tenant's conversations, and a previous tenant keeping a way back into the account.
Active-session enforcementEvery 6 hours, for each rented account, the platform lists the account's active Telegram sessions. Unknown sessions raise a security notification with two actions, "It's me" and "Terminate". Nothing is terminated until you decide, except sessions approved by a previous tenant, which are revoked when the account changes hands.Unauthorized access to a rented account through a session opened outside the platform.
Hidden service peersConversations with Telegram's system account (peer 777000, plus any IDs in the hidden_telegram_peers table) are filtered out by the inbox API for rented accounts only. Owned accounts always see them. The filter is applied on read; the rows themselves are stored.A rental holder reading login codes for the underlying phone number.

Authentication

Password requirements

RequirementValue
Minimum length12 characters
Maximum length72 bytes (bcrypt limit); non-English letters and emoji take more than one byte
Character mixNo composition rules; the password may not equal your email or name
Breached passwordsNew passwords are checked against the Have I Been Pwned list; only the first 5 characters of the SHA-1 hash are sent, and the check is skipped if that service does not answer within 2 seconds
Existing passwordsPasswords set under the earlier rules keep working until they are changed
Storagebcrypt, cost factor 12
Social-login accountsMay have no password until one is set in Settings

A failed login takes the same time whether the email exists or not, so the login endpoint cannot be used to enumerate accounts.

Sessions and tokens

ItemValue
Access tokenRS256 JWT, 15 minutes
Refresh tokenRandom 512-bit token, 7 days, rotated on every use with a 30-second reuse grace
CookiesHttpOnly, Secure, SameSite=None; the refresh cookie is scoped to /auth
Server-side session recordsExpired records are pruned after 7 days
API keystgp_ prefix, stored as SHA-256 hash, default expiry 90 days, at most 20 per user
Deactivated usersA user with is_active = false is refused at login, refresh and API-key authentication

Two-factor authentication (TOTP)

SettingValue
AlgorithmTOTP (RFC 6238), 6 digits, 30-second step, one step of clock drift accepted
Replay protectionA code that was already accepted is rejected for the rest of its window
Backup codes8 codes, each valid once, stored as SHA-256 hashes
Secret storageFernet-encrypted at rest when a TOTP encryption key is configured
Attempt limit10 failed attempts per account, then a 15-minute lock regardless of source IP

Rate limiting

EndpointLimit
POST /auth/login10 requests per minute per IP
POST /auth/register5 requests per minute per IP, plus the email-send cap below
POST /auth/forgot-password, /resend-verification5 emails per hour per IP; per address: one send per 60 seconds, 10 per day
POST /auth/reset-password, /verify-email30 requests per minute per IP; each code is valid 10 minutes and dies after 5 wrong attempts
Team mutations (invite, update, remove)Per-user mutation limiter

Limits use an atomic sliding window in Redis. When a limit is exceeded the platform returns HTTP 429 with a Retry-After header equal to the window length. There is no application-level lockout after repeated 429s. At the edge, the platform sits behind Cloudflare, nginx terminates TLS 1.2/1.3 with HSTS, and fail2ban bans hosts that generate malformed requests.

Encryption

At rest

DataEncryption
Telegram session stringsFernet (AES-128-CBC + HMAC-SHA256) under the platform session key. Optional envelope format: a per-account data key wrapped either by the platform key (standard) or by an Argon2id key derived from the owner's passphrase (vault mode, see below).
Proxy credentialsFernet, same platform key
Bot API tokensAES-256-GCM with a key derived via HKDF-SHA256 from the platform master secret
Outbound webhook signing secretsFernet
TOTP secretsFernet, when a TOTP encryption key is configured
Passwords, backup codes, refresh tokens, API keysStored only as hashes (bcrypt for passwords, SHA-256 for the rest)
Message bodies, conversation metadata, contactsNot encrypted at the application layer; plain rows in Postgres
Attachments (MinIO, 20 MB per file)Not encrypted server-side; access goes through a tenant-scoped streaming endpoint
Database backupsNot encrypted; stored on local volumes on the database host, with no off-site copy

There is no external KMS and no automated key rotation. Encryption keys are operator-managed environment secrets, kept outside the database. Rotating a key is a manual operation.

Vault mode (passphrase-encrypted sessions)

For owned accounts you can re-wrap the session under a passphrase (minimum 10 characters). The wrapping key is derived with Argon2id (t=3, m=64 MiB, p=4); the platform key can no longer decrypt the session, so a database dump is useless without the passphrase. The trade-off: automation only runs while an unlock window is open (default 1 hour, maximum 24 hours). The derived key is handed to the worker in memory and never persisted. Session export is refused while vault mode is on. Rented accounts cannot use vault mode.

In transit

All public traffic is HTTPS (TLS 1.2 or 1.3, HSTS with preload) behind Cloudflare. Telegram traffic leaves through the account's proxy as MTProto, which is encrypted end-to-server by Telegram's own protocol.

Connection and session monitoring

  • Every Telegram connect attempt per account is written to connection_audit_log with the proxy host and port, API ID, device model, connection type, success or error, duration and whether the connection came from a server IP.
  • Rented accounts get the 6-hour active-session scan described above; pending prompts appear under Notifications with type security.
  • There is no geolocation-based alerting, no device-fingerprint drift detection and no automatic session kill based on health score.

Audit trail

LogWhat is recordedWho can read itRetention
Team audit logteam.create, team.member.invite, team.member.update, team.member.remove, team.member.leave with actor, IP address, user agent and a before/after snapshotOwner and admin (audit:logs:read); every user can read their own actionsKept indefinitely
Platform audit logsession_export, proxy lifecycle (proxy_created, proxy_updated, proxy_deleted, proxy_assigned, proxy_unassigned, proxy_rotated), session enforcement (enforcement_check, prompt_emitted, check_failed)OperatorsKept indefinitely
Connection auditPer-connect record described aboveOperatorsKept indefinitely
Account health logHealth-score inputs per accountOperators90 days
Webhook deliveriesEvent type, payload, HTTP status, first 400 bytes of the response, attemptsWorkspace7 days for delivered, 30 days for failed

The team audit log is available at GET /audit/logs (filter by team_id, action, resource_type). See API.

SSRF protections

The platform makes outbound HTTP requests on your behalf in four places. Each has its own guard:

CallerGuard
Event webhooks (inbox)New URLs must be https://. The host is resolved at registration and again before every delivery, and the delivery is sent to that vetted address; loopback, private, link-local, carrier-NAT, reserved and multicast addresses (IPv4 and IPv6), localhost and *.local are refused. A stored endpoint that now resolves to a refused address fails with a clear error instead of being delivered. Deliveries do not follow redirects, time out after 10 seconds, retry up to 6 times over about 8 hours, and the endpoint is switched off after 5 consecutive final failures.
Bot webhook URLResolved via DNS at registration; refused if any resolved address is private, loopback, link-local, multicast, unspecified or reserved.
Flow HTTP-request nodeResolved at request time and the connection is pinned to the validated public IP (closes DNS rebinding); 15-second total and 5-second connect timeout. Requests are not signed.
API connectorshttp:// or https:// only. The host is resolved when the connector is saved and tested, and the request is sent to that vetted address; loopback, private, link-local, carrier-NAT, reserved and multicast addresses (IPv4 and IPv6, including IPv4-mapped and numeric encodings), localhost and *.local are refused. Redirects are not followed.
Checkout redirect URLsResolved and refused if they land on a private or loopback address.

Plain http:// webhook targets are accepted; use HTTPS wherever you can.

Bot webhook verification

Inbound bot updates are verified with the per-bot X-Telegram-Bot-Api-Secret-Token secret (see Bots). Outbound event webhooks are signed: every delivery carries X-TGP-Signature, an HMAC-SHA256 of the timestamp and raw body under the endpoint's secret, plus X-TGP-Timestamp and X-TGP-Delivery — see API › Webhooks. Requests made by a flow's HTTP-request node are not signed; protect those endpoints with a secret path or an allowlist.

Reporting a vulnerability

Report through in-app Support (the Support bubble in the dashboard), or via @TelOsNotif_Bot if you do not have an account. There is no security mailbox. Include reproduction steps and the affected endpoint. Do not test against accounts or workspaces you do not own. Scope, out-of-scope items and the response targets (acknowledgement within 24 hours, verified high-severity issues targeted for remediation within 14 days) are on /security. See also Support.

Report an issue with this page