· 6 min read

Threat Modeling in Telegram Bots: Comparing Client-Side Trust vs Server-Side Verification

Telegram bot and app developers often assume that platform boundaries provide enough safety, but most real security failures come from where you trust input, state, and identity. This comparison breaks down client-side and server-side approaches so you can choose the safer pattern for commands, payments, callbacks, and session handling.

If you build for Telegram, the security problem is usually not encryption or transport. It is trust: what you believe about a user, a message, a callback, or an event before you act on it.

Two common engineering approaches show up again and again:

  1. Client-side trust: accept data from the bot UI, message text, or callback data and treat it as authoritative.
  2. Server-side verification: treat all Telegram-originated input as untrusted until your backend checks it against its own state.

For Telegram developers, the second approach is almost always safer. The first is often faster to build, but it creates security bugs that are hard to notice in testing and easy to exploit in production.

What “client-side trust” looks like in Telegram projects

In a Telegram bot context, “client-side trust” usually means relying on values that came through the user-facing path without re-checking them on the server. Examples:

  • trusting callback_data to contain a valid action ID
  • trusting message text to identify a user or account
  • trusting a hidden field in a web app form without server validation
  • trusting that a button press came from the same user who saw the button
  • trusting that a command payload has not been replayed or modified

This is convenient because it makes handlers short:

if callback_data == "approve_invoice_123":
    mark_invoice_paid(123)

The bug is obvious in hindsight: the callback string becomes the source of truth. If an attacker can forge, replay, or abuse that state, the backend may perform an action that was never meant for them.

Risks of client-side trust

  • Tampering: the client can send unexpected input.
  • Replay: an old valid action may be reused later.
  • Privilege confusion: one user can trigger an action intended for another.
  • State drift: the bot UI says one thing, but the backend has moved on.
  • Injection into business logic: IDs, roles, or amounts can be swapped if not validated.

For Telegram developers, the key mistake is assuming that because an action came through Telegram, it is safe. Telegram is the transport, not your authorization layer.

What “server-side verification” looks like

Server-side verification means your backend keeps the authoritative state and checks every sensitive action against it.

Examples:

  • map callback tokens to records in your database
  • verify the Telegram user ID against a session or ACL before acting
  • confirm a payment or order status on the backend before fulfillment
  • store one-time nonces and reject duplicates
  • check timestamps and expiration windows for deep links or web app payloads

A safer pattern looks more like this:

record = db.get_callback_record(token)
if not record or record.used or record.expires_at < now():
    reject()

if record.telegram_user_id != incoming_user_id:
    reject()

perform_action(record.action)
mark_record_used(token)

This is less convenient, but it gives you control. The server decides what is valid, not the chat client, not the button payload, and not the last message text the user sent.

Benefits of server-side verification

  • Strong authorization: actions are tied to known identities and roles.
  • Replay resistance: one-time tokens can be invalidated after use.
  • Better auditing: you can log decisions and trace failures.
  • Safer recovery: if a bot restarts, your state still exists.
  • Clearer boundaries: business rules live in one place.

Comparison: where each approach breaks down

1. Callback buttons

Client-side trust: You encode all the action details in callback_data and execute them directly.

Problem: Anyone who can trigger the callback path may be able to reuse or manipulate that data, especially if the bot assumes the payload is authentic and current.

Server-side verification: Use a short opaque token, store the action server-side, and bind it to the user, chat, and expiry.

Why it is better: Even if the token leaks, it is useless without a matching server record and valid context.

Client-side trust: A /start <payload> parameter maps directly to a reward, invite, or account link.

Problem: Payloads can be shared, replayed, or used by the wrong user if you do not validate ownership and expiry.

Server-side verification: Treat the payload as a lookup key only. Verify that it belongs to the Telegram user who is trying to redeem it.

Why it is better: Referral codes, invite flows, and onboarding become resilient to simple replay abuse.

3. Telegram Web Apps

Client-side trust: A web app posts user-selected values to your backend, and you trust the form contents because the UI was inside Telegram.

Problem: UI origin is not the same as server authorization. Form values can still be tampered with unless the backend validates the signed data and the intended user.

Server-side verification: Verify the signed initialization data and then separately validate the business action against your own state.

Why it is better: You avoid treating a signed launch context as permission to complete sensitive operations.

4. Payments and fulfillment

Client-side trust: After a user clicks a payment button, the bot immediately delivers the product.

Problem: The bot may fulfill before it has confirmed the payment state and the order match.

Server-side verification: Only fulfill after the server has confirmed the payment event and matched it to the right order, amount, and user.

Why it is better: It blocks fake or mismatched completions and prevents accidental double fulfillment.

Practical decision rule for Telegram developers

Use this rule:

  • If an action affects money, access, identity, or irreversible state, verify it on the server.
  • If the data is only for display or convenience, client-side handling may be acceptable.

A helpful test is: “Would I still trust this if the user copied it into another chat, another session, or another device?”

If the answer is no, do not trust it as-is.

Use opaque identifiers, not meaningful payloads

Avoid putting raw business rules into callback data.

Bad:

callback_data = "delete_user:42:admin"

Better:

callback_data = "token_7f9c2a"

Then resolve that token server-side.

Bind actions to user and chat context

When possible, store:

  • Telegram user ID
  • chat ID or context ID
  • action type
  • expiration time
  • usage status

This makes it much harder to reuse a valid action in the wrong context.

Make actions idempotent

Telegram bots can receive retries, duplicate updates, or repeated clicks. Design sensitive handlers so the same action cannot be applied twice.

Reject stale state

If a message menu was rendered 10 minutes ago and the user’s permissions changed, do not assume the old buttons are still valid.

Log verification failures

Security bugs are easier to detect when you log:

  • token mismatches
  • expired actions
  • unauthorized user IDs
  • duplicate submissions
  • impossible state transitions

A simple mental model

Think of Telegram as a delivery channel, not a trust boundary.

  • The client side is where users interact.
  • The server side is where truth lives.
  • The secure design is the one that assumes the UI can lie, get stale, or be replayed.

That mental model helps more than any framework-specific trick.

Bottom line

For Telegram developers, the comparison is not really “client-side vs server-side” in the abstract. It is convenience vs control.

Client-side trust is faster, but it puts sensitive decisions in the wrong place. Server-side verification takes a little more code, but it protects callbacks, deep links, web apps, and payments from tampering, replay, and privilege mistakes.

If your bot touches anything valuable, make the server the source of truth every time.

← All posts