In this article
A public bot is a program that anybody in the world can send input to, forever, for free. That is the entire security model to start from, and most of the defences that matter follow from taking it literally.
There are three things worth protecting and they are not equally important. The token, the data, and your ability to keep sending — in that order, because the first one loses you everything and the last one is merely annoying.
What you will be able to do after this step
- check_circle Say what verifying a delivery proves, and what it does not.
- check_circle Identify a sender correctly, rather than by something they chose.
- check_circle Build the half of rate limiting people usually skip.
- check_circle Reduce what a compromise would actually be worth.
Your webhook checks the shared secret on every delivery. Is the data inside it now trustworthy?
The secret answers exactly one question: did this request come from Telegram rather than from somebody who found your address. It says nothing about the contents, because the contents are whatever a stranger typed into your bot — a display name, a message body, a file name, a caption, the text on a button they were shown. Every one of those is user input from the open internet, and it ends up in your logs, your database, your templates and quite possibly an administrator's screen. Treat an update the way you would treat a form submitted by an anonymous visitor, because that is precisely what it is.
A message arrives with your colleague's exact name and profile photograph, asking the bot to run an administrative command. What should the bot check?
Names and photographs are free to copy and take seconds to set, which is the whole of the impersonation problem from Step 065. Usernames are better, because they are unique — but they can be released and taken by somebody else, and a look-alike differing by one character is a well-worn trick. The numeric identifier is assigned rather than chosen, does not change, and is what authorisation should be based on. This is not theoretical: a bot that grants privileges based on a name is a bot that grants privileges to whoever sets that name, and the attempt costs the attacker nothing.
Your token was in a public repository for six months. Nothing bad appears to have happened. What now?
"Nothing visible happened" is the expected appearance of the most valuable kind of misuse. A token lets its holder receive what your bot receives, and reading leaves no trace you would ever see — no messages sent, no posts changed, nothing out of place. Public code is scanned for exactly these strings automatically and continuously, so the realistic assumption is that it was found. Revoke it now (Step 086), work out what it could reach during that window, and treat anything sensitive that passed through the bot in that period as having been seen.
security The endpoint, and the gate that is worth having
If your bot is called rather than asking (Step 091), its address is on the public internet and will receive traffic from people who are not Telegram. Two defences, in order.
The shared secret. Telegram can include a value of your choosing with every delivery. Check it before parsing anything, reject what fails, and compare it in a way that does not leak timing. It is cheap, it is designed for this, and it removes forged deliveries entirely.
Treat the contents as hostile anyway, for the reason in the first quiz item.
Filtering by network address is sometimes suggested as a third gate. It can help, with one caution worth stating: the published ranges change. A hard-coded list in a configuration file is a future outage with no obvious cause, so if you do this, pull the ranges from the published source automatically, and never let it be the only thing standing between the internet and your handler.
speed Rate limiting has two halves
People build the incoming half and discover the outgoing one during an incident.
Incoming: what one person can make you do. Somebody holding down a command is not a sophisticated attack; it is a bored user or a broken client. A limit per account, applied before you do any work, keeps that from consuming your database connections or your budget with whatever external service you call. Note what it does not do: it does not stop them sending. Their messages still arrive; you are choosing not to act on them.
Outgoing: what you can make Telegram do. This is the half that actually bites. Sending is paced, both overall and particularly into a single chat, and a bot that broadcasts to a list, replies in a loop, or floods an error channel will be told to wait. Respect the wait. A retry loop that ignores it turns a brief pause into a long one, and the classic version of this is an error-reporting channel that stops working during the exact incident it exists for, because it tried to send four thousand identical messages.
The practical shape for anything that sends in bulk is a queue with a deliberate pace, a record of what was sent to whom, and the ability to resume — which is the same conclusion Step 093 reaches from a different direction.
manage_search Scraping is a design question, not a detection one
If your bot will answer any query from anybody, it is a public interface to your data, and somebody will eventually walk it end to end. Detecting that is an arms race you fund; deciding what is available in bulk is a one-time decision.
The useful question is not "how do I stop scraping" but "what would a complete copy of what this bot hands out be worth, and to whom?" A bot that converts units has nothing to take. A bot that will look up any record by identifier is a database with a chat interface, and the fact that a human has to type each query is not a defence when the human can be a script.
Three design choices cover most of it. Return one thing at a time rather than lists. Require something the requester has to possess for anything sensitive — an order number rather than an email address, a code rather than a name. And do not expose an identifier that can be counted upwards, which is how sequential lookups become a full export.
touch_app Try it now — five minutes
Four steps, and none of them requires new infrastructure.
- Find where your token is and say it out loud If the sentence includes a file you edit, a screenshot, or a message to a colleague, that is today's task.
- Search your own code for a check on a name Anywhere authorisation depends on a display name or a username, replace it with the numeric identifier.
- Send your bot something absurd A very long message, a file with a strange name, text full of markup characters. Watch what your logs and your templates do with it.
- Look at what you store and delete a column The cheapest security work available is holding less. Step 093 has the argument; this is the five-minute version.
key_off Reducing what a compromise is worth
Most of the effort in this area goes into keeping people out. The more reliable investment is making the inside less valuable, because it works whether or not the keeping-out succeeded.
One bot per purpose. A token that can post to your main channel, moderate your group and run your shop is one leak away from all three. Separate bots with separate tokens and narrow rights turn a total compromise into a contained one, and the cost is a few minutes in BotFather.
Narrow rights, reviewed. Step 086's rule applies here as an ongoing habit rather than a setup step: the bot that was granted deletion rights for a one-off cleanup still has them, and the administrator list of an old channel is where forgotten credentials live.
Store less, and expire what you store. A conversation log kept forever is the thing that makes a breach a story. Data you never kept cannot be taken.
Rotate on people, not on a schedule. The realistic moment for a token to leak is when somebody leaves, a laptop is replaced, or a contract ends. Tying rotation to those events is worth more than a calendar reminder nobody honours.
help Common questions at this stage
Can somebody find my webhook address?
Assume yes. It appears in deployment configuration, in logs, in screenshots, and it is guessable if you named it predictably on a domain that resolves. Treating the address as a secret is not a strategy; verifying the shared secret and distrusting the contents is, and it holds whether or not anybody found it. The one thing worth doing is not putting the address anywhere public, which is hygiene rather than defence.
How do I block an abusive user?
Keep a list of accounts your bot ignores, and check it at the very start of handling, before any work. You cannot stop them sending — their messages will keep arriving — but you can make them cost nothing. In a group, the room's own tools are the better instrument: restricting or removing them is a moderation action with an audit trail, which is what Step 059 exists for.
What does it look like when I hit a sending limit?
Sends start failing with an instruction to wait a specific period, and that period grows if you keep trying. The correct handling is to stop, wait what you were told, and resume — which means your sending path needs to be able to pause rather than being a loop that retries immediately. Bots that ignore this spend their lives in a throttled state and appear, to their users, to be randomly unreliable.
Should I put a CAPTCHA in front of my bot?
Almost never for a bot itself — it adds friction for every honest user to inconvenience an attacker who can pay somebody to solve it. Where a challenge does earn its place is at the door of a group, as Step 064 describes, because there the thing being stopped is bulk automated joining rather than an individual using your service. If a bot is being abused by volume, a per-account limit and an ignore list do more with less cost to everybody else.
Is it safe to log the full update?
It is convenient and it means your log files contain everybody's messages, including anything sent to you by mistake. If you need them for debugging, keep them briefly and separately from your ordinary application logs, and redact obvious sensitive fields. The test is simple: if a copy of that log directory appeared somewhere public, what would be in it? Answering honestly usually changes what gets logged.
Do I need a secrets management system?
For one bot on one server, environment variables set by your hosting are entirely adequate, and reaching for heavier tooling is a way of feeling secure rather than being it. What matters is the properties, not the product: the secret is not in your code, not in your repository, not in your image, and can be changed without a code change. When you have many secrets and several people, a dedicated system starts paying for itself — before that, it is one more thing to run.
At a glance
- 1What verification proves
A shared secret proves who delivered a request, not that its content is safe.
- 2Identify by ID
Check the numeric user ID, not a name or username.
- 3If the token leaks
Revoke it at once; quiet misuse leaves no trace.
Updated