コンテンツにスキップ

Interaction security pitfalls

このコンテンツはまだ日本語訳がありません。

Status Stable · Last verified 2026-07-12 · API v10 · Sources Interactions overview, Receiving & responding

Interactions come from the internet. Treat every field as attacker-influenced until you’ve verified and authorized it. These are the recurring failure modes.

1. Not verifying the signature (or verifying the wrong bytes)

Section titled “1. Not verifying the signature (or verifying the wrong bytes)”

The top mistake on HTTP endpoints. Discord probes with invalid signatures and disables endpoints that accept them. Verify Ed25519 over timestamp + rawBody, against your Public Key, before parsing — never “fail open.” Full flow: Signature verification.

Related foot-guns on the same path:

  • Accepting a stale signed request (no timestamp skew check) → replay of a captured interaction. Cap |now − timestamp| (example: 5 minutes).
  • Buffering unbounded bodies before verification → memory DoS. Cap body size early (example: 256 KiB) and return 413.
  • Fail-open on bad hex / bad key import → treat crypto errors as 401, never as “assume valid.”

2. Trusting custom_id, option values, or modal input

Section titled “2. Trusting custom_id, option values, or modal input”

custom_id, command option values, and modal fields are client-influenced. Autocomplete suggestions are hints — a client can submit any value, not just what you offered.

  • Re-validate every submitted value server-side (type, range, allowed set).
  • Treat custom_id as untrusted routing data. If you encode an id ("cart:remove:42"), re-check that this user may act on resource 42.
  • Never build SQL/shell/HTML from interaction input without escaping.

3. Confusing “who can use it” with “who is allowed to do it”

Section titled “3. Confusing “who can use it” with “who is allowed to do it””

default_member_permissions controls default visibility in the UI — it is a convenience, and server admins can override it. It is not your authorization check.

  • Enforce permissions in your handler using the interaction’s member.permissions (and re-fetch authoritative state for anything sensitive).
  • Don’t assume the invoker is an admin because your command is “admin-only” in the portal.

A user-installed command can run in a DM, a group DM, or a server where your bot is not a member — so guild-only assumptions break.

  • Read the interaction context and authorizing_integration_owners to know whether you’re in a guild-install ("0" → guild id) or user-install ("1" → user id) context. See OAuth2 & installation.
  • Don’t call guild-only APIs when there is no guild.

Slow handlers that reply late show “This interaction failed” to the user. Defer (type 5/6) immediately, then do the work and edit the response. See Responses & follow-ups.

Interactions can be redelivered. A handler that grants a reward or charges money on every delivery double-acts. Key side effects by interaction/resource id and make them safe to repeat. See lifecycle → retries.

  • Put errors, tokens, and personal data in ephemeral replies (flag 64), not public messages.
  • Never log the interaction token (it grants follow-up posting for ~15 minutes) or any bot/client secret.
  • Scrub user input before echoing it back (mention-injection, markdown, @everyone — set allowed_mentions explicitly).

Command apps rarely need the MESSAGE_CONTENT intent or Administrator. Keep intents minimal and compute an explicit permission bitfield. Fewer capabilities = smaller blast radius if a token leaks.

A public button can be spammed. Rate-limit per-user/per-command in your handler, and honor Discord’s own limits when you post follow-ups (parse 429/retry_after).

  • Signature verified over the raw body; 401 on failure.
  • Every option / modal / custom_id value re-validated and authorized.
  • Real permission checks in-handler (not just default_member_permissions).
  • Install context (authorizing_integration_owners) handled.
  • ACK within 3s (defer for slow work); handlers idempotent.
  • Sensitive output ephemeral; tokens/secrets never logged.
  • Minimal intents/permissions; per-user rate limiting.