Slash Commands Need Authorization Boundaries
Slack, Discord, Telegram, and internal chat bots make commands feel lightweight. If a command can export data, change access, or trigger infrastructure, it needs the same authorization discipline as an API endpoint.
Slash commands turn chat into a control plane. A user types a short command, the platform sends structured context to an app, and the app may answer in the room, open a modal, query an internal system, rotate an invite link, export a channel, deploy a service, or change a user's role. The chat UI makes the action feel informal, but the backend receives an authenticated request that can carry real authority.
The safer pattern is to treat every command as an API entry point with a social interface. Verify that the request came from the platform. Then authorize the specific user, tenant, room, command, and resource before any side effect. Parse command text as untrusted input, keep acknowledgement separate from execution, prefer private responses for sensitive results, log both denials and approvals, and test abuse paths as carefully as the happy path.
Key Takeaways
- check_circle A slash command is not just a UI shortcut. It is an inbound request that can trigger backend work.
- check_circle Platform request verification proves origin, not whether the actor is allowed to run the command.
- check_circle Authorization needs stable IDs for workspace, server, channel, user, role, and target resource.
- check_circle Free-form command text should be parsed with strict grammars and never passed directly into shell, SQL, admin APIs, or ticket queries.
- check_circle Fast acknowledgement should not mean fast side effects. Sensitive commands need queues, idempotency, confirmation, and audit logs.
- check_circle Denied commands are security telemetry and should be logged with enough context to find probing or drift.
A Command Is An API Entry Point
Slack describes slash commands as a way for users to invoke an app from the message composer. When a command is submitted, Slack sends the app an HTTP POST request with context such as the team, enterprise, channel, user, command, text, response URL, trigger ID, and app ID. Discord application commands have their own command objects, installation contexts, interaction contexts, and permissions. Telegram bots can expose commands that users run in chats and groups. In each case, the command is a compact chat action that crosses into application code.
That boundary is easy to underestimate because no one is opening a separate admin console. A moderator may type a command in a community room. A support engineer may run a customer lookup from Slack. A release manager may trigger a deployment from a private channel. A crypto community may rotate a token-gated role through a bot. The command text is short, but the system behind it may hold customer data, repository access, wallet allowlists, moderation history, or production credentials.
Origin Verification Is Only The First Gate
The first question is whether the platform actually sent the request. Slack tells developers to use request signing rather than the older verification token, and its command payload includes fields that help bind the request to the app and workspace. That protects the public endpoint from a basic forged HTTP request. It does not decide whether the invoking user should be able to perform the requested action.
Authorization has to happen inside the product. A request can be legitimate and still be unauthorized. A real Slack user may run a command from the wrong channel. A Discord command may be visible in a context where the backend should deny a state change. A Telegram bot may receive a command in a group where the sender is not an operator. Treat platform verification as authentication of the envelope. Treat command authorization as the decision about the action.
Scope The Tenant, Room, Role, And Resource
The command handler should map platform context to internal authorization before doing work. For Slack that means using stable IDs such as team ID, enterprise ID, channel ID, user ID, and app ID instead of mutable names. For Discord it means checking the guild, command context, configured command permissions, and the user's effective role before trusting the interaction. For Telegram it means checking the chat, sender, command, and bot configuration, especially in groups where users may share the same surface but not the same duties.
The target resource also matters. A user who can run `/export #announcements` may not be allowed to export a private incident channel. A moderator who can run `/ban` in one community may not be allowed to ban members in another server. OWASP classifies missing function-level authorization as a common API failure because legitimate users can sometimes reach functions they should not have. Slash commands compress that risk into a friendly chat prompt.
Command Text Is Untrusted Input
Slack's command payload includes a `text` field that contains everything after the command name. Slack can escape user, channel, and link mentions into IDs, which is useful, but the rest of the text is still attacker-controlled input. Discord and Telegram commands also carry parameters supplied by users. A command parser should accept a narrow grammar, reject ambiguous input, normalize IDs, and validate every referenced object after parsing.
The highest-risk pattern is piping command text into another interpreter. A chat command should not become a shell command, SQL fragment, LDAP filter, GraphQL query, cloud CLI string, or ticket-search expression without strict construction. Even when the command only calls an internal API, the backend should build the request from validated fields, not from text pasted by the user. Sensitive operations should use explicit confirmation steps and show the actor exactly what will happen before the side effect runs.
Acknowledge Quickly, Execute Deliberately
Slack requires command acknowledgement within a short window, and the docs distinguish between acknowledging receipt, responding immediately, and doing more work later. That timing requirement is a product constraint, not a reason to skip controls. The handler can verify the request, enqueue a job, return a private acknowledgement, and then perform the slower authorization, confirmation, rate-limit, and side-effect path in a controlled worker.
Public versus private responses are part of the security design. Slack command responses can be ephemeral by default or posted into the channel with `in_channel`. Sensitive results such as export links, customer lookups, ban reasons, incident IDs, invoice state, or invite rotations should not be posted publicly by accident. Response URLs, interaction tokens, and follow-up hooks should be treated as scoped capabilities. They should not be stored as general-purpose webhooks or reused outside the interaction they were issued for.
Logs Should Show Denials Too
Command logs should include the tenant, channel, user, command, target resource, authorization decision, job ID, and response visibility. They should avoid storing sensitive free-form arguments unless there is a clear need and retention policy. A log that only records successful commands misses the early warning signs: users trying admin-only commands, commands issued from unexpected rooms, repeated malformed arguments, and attempts to target resources outside a user's scope.
Testing should cover the same cases. Try the command from a direct message, a public room, a private room, an external shared channel, an account with no role, a stale guest account, and a user who has the right role in one tenant but not another. Replay old requests. Change channel names. Remove the user's role between acknowledgement and execution. The test suite should prove that the command fails closed when any piece of context is missing, stale, or outside policy.
Checklist
- Verify platform signatures, timestamps, and app identifiers before parsing the command.
- Authorize the user, tenant, room, command, and target resource inside the backend, not only in platform configuration.
- Use stable platform IDs and internal resource IDs rather than channel names, display names, or pasted text.
- Parse command arguments with a strict grammar and build downstream requests from validated fields.
- Use private responses for sensitive output and require confirmation for destructive or data-exporting actions.
- Log denied and approved commands with enough context to support incident response and access reviews.
Sources
- Slack Developer Docs: Implementing slash commands open_in_new
- Slack Developer Docs: Verifying requests from Slack open_in_new
- Discord Developer Docs: Application Commands open_in_new
- Telegram Bot Features: Commands open_in_new
- OWASP API Security Top 10: Broken Function Level Authorization open_in_new
- OWASP Cheat Sheet Series: Authorization open_in_new
Continue Reading
Guest Accounts Need Expiration Dates
Contractors, partners, clients, outside collaborators, and community helpers often need temporary access to chat, docs, repos, and tools. Without a sponsor, scope, review, and end date, guest access becomes quiet permanent access.
Screen Sharing Needs A Data Boundary
Slack huddles, Teams, Google Meet, Zoom, and browser screen-capture APIs make sharing fast. They also turn private tabs, alerts, audio, files, and recording controls into collaboration data risk.
Chat Exports Need Access Controls
Slack, Discord, Telegram, Google, WhatsApp, and Teams all give users or admins ways to pull conversation data out of the live app. Export policy decides whether history becomes evidence, migration material, or a new leak.