Telegram Bot Security: Protecting Bot Tokens, Webhooks, and Secrets in Production #
Security is often overlooked when developing Telegram bots—until a token gets leaked, a bot gets hijacked, or someone abuses an unauthenticated webhook.
In this article, we cover the top security best practices every Telegram bot engineer must implement before launching to production.
1. Never Hardcode Bot Tokens in Source Code #
Hardcoding tokens directly into bot.php is the #1 reason bots get compromised. Use environment variables or protected configuration files:
// Bad:
$botToken = '719283749:AAFkj4829ksj-dklasjd';
// Good:
$botToken = getenv('BOT_TOKEN');
if (!$botToken) {
throw new RuntimeException("BOT_TOKEN environment variable is not configured.");
}Botlify provides an integrated Environment Variable Manager in the dashboard where you can securely store BOTTOKEN, ADMINCHAT_ID, and database credentials without ever exposing them to git repositories.
2. Restrict Sensitive Commands to Admin Chat IDs #
Admin commands such as /broadcast, /stats, /ban, or /restart must always verify the sender's Telegram chat_id:
$adminChatId = (int)getenv('ADMIN_CHAT_ID');
$senderChatId = (int)($message['chat']['id'] ?? 0);
if ($senderChatId !== $adminChatId) {
// Silently ignore or deny
exit;
}
// Proceed with admin privileged actionusername (e.g. @johndoe) because usernames can be changed or impersonated. Telegram's numerical id is permanent and immutable.3. SQL Injection Prevention in Bot Databases #
If your bot stores user states, points, or messages in SQLite or MySQL, always use prepared statements:
// Vulnerable to SQL injection:
// $db->query("SELECT * FROM users WHERE chat_id = " . $chatId);
// Secure with Prepared Statement:
$stmt = $db->prepare("SELECT * FROM users WHERE chat_id = ?");
$stmt->execute([$chatId]);
$user = $stmt->fetch();4. Rate Limiting Bot Users #
Spam attacks from malicious users can flood your bot and hit Telegram's global 30-message-per-second limit. Implement a simple rate limiter in memory or SQLite:
function is_rate_limited(PDO $db, int $chatId, int $maxPerMinute = 20): bool
{
$now = time();
$minuteAgo = $now - 60;
// Clean old logs
$db->prepare("DELETE FROM user_rate_limits WHERE timestamp < ?")->execute([$minuteAgo]);
// Count recent
$st = $db->prepare("SELECT COUNT(*) FROM user_rate_limits WHERE chat_id = ?");
$st->execute([$chatId]);
$count = (int)$st->fetchColumn();
if ($count >= $maxPerMinute) {
return true;
}
$ins = $db->prepare("INSERT INTO user_rate_limits (chat_id, timestamp) VALUES (?, ?)");
$ins->execute([$chatId, $now]);
return false;
}Summary #
Securing your bot requires minimal effort but protects you and your community from devastating leaks. Store tokens in environment variables, verify numerical chat IDs, use prepared queries, and configure webhook secrets.