AWRA OpsHub Search

A Webhook URL Is a Password

A Slack incoming webhook has no signature, no key and no account behind it. Whoever holds the URL can post to that channel, forever, as you — which makes it a credential wearing a link's clothing, and it usually gets treated like a link.

Integrations & Data AWRA OpsHub Team 11 min read

There is a category of secret that does not look like one. An API key looks like a secret: it is a meaningless string in a field marked key, and people treat it accordingly. A webhook URL looks like a link. It starts with https, it has a domain you recognise, and it goes in a field that often is not even a password field. It gets pasted into tickets, screenshots, shared docs and chat messages by people who would never do that with a key, and it does exactly the same job.

What the URL actually authorises

A Slack incoming webhook is a long unguessable address bound to one channel in one workspace. Post JSON to it and a message appears there. That is the whole protocol.

There is no signature on the request. No shared secret to compute a digest with. No account, no token, no expiry, and no way for Slack to tell one sender from another, because there is nothing in the request that identifies a sender at all. Possession is the entire authorisation.

Which is not an oversight — it is a reasonable design for the thing it does. But it has consequences that follow directly, and they are worth writing down rather than deriving under pressure.

If the URL leaks What the holder can do What they cannot
Anyone with it can post Put any message in that channel, indefinitely Read the channel, or anything else in the workspace
Messages appear as your integration Look exactly like your genuine alerts Reach a different channel
There is no rate limit you set Flood the channel until it is muted Delete or edit existing messages
Nothing expires Keep doing it after they have left the company Do anything once the webhook is revoked

The realistic risk is not somebody reading your data. It is somebody posting a convincing approval request into a channel your team trusts.

That last one is the part worth taking seriously, and it is a social risk rather than a technical one. A channel that has received genuine approval alerts for eight months is a channel where a ninth one is believed. Nothing about a forged message would look wrong, because there is nothing in a real one that a forgery lacks.

The reach of a leaked webhook is fixed and small — which is a real security property worth knowing precisely.
The reach of a leaked webhook is fixed and small — which is a real security property worth knowing precisely.

How it is handled here

Given that, the URL is treated as what it is rather than as what it looks like.

It is stored encrypted against your workspace, on the row for that connector. It is never written into a log line — when a send fails, what gets logged is the provider's error message, and the address is not part of the context. It is never included in an error shown to a user, and it is not echoed back into the page in full once saved.

Configuring it is a permissioned action rather than something any signed-in member can do, which matters in both directions: it stops a webhook being replaced by somebody who should not be redirecting your alerts, and it keeps the number of people who have ever seen the value small.

None of that is exotic. It is the ordinary handling of a credential, applied to something that does not present as one — which is the entire trick, because the failure mode here is not a weak control, it is a control nobody thought to apply.

Four connectors with this shape, and two without

Of the six chat destinations, four are webhook-shaped and identical in this respect: paste a URL, and the URL is the credential. Slack, Teams, Discord and Google Chat all work this way, and each is bound to exactly one destination — a channel, a space — chosen when the webhook was created.

That binding is a genuine security property and it is easy to undervalue. A leaked webhook reaches one channel. It cannot be pointed at a different one, cannot be used to enumerate anything, and cannot read a single message. Compared with a token that grants access to a workspace, the blast radius is small and fixed.

The two exceptions in the family carry real credentials instead, and the difference shows up in how they have to be handled.

  1. Telegram carries a bot token in the address

    The token is part of the request path rather than a header. It is a credential in a position where things are conventionally logged, which is a reason to treat proxy and access logging as part of the threat model rather than as plumbing.

  2. A Telegram bot token is broader than a webhook

    It is the bot, not a channel. Anyone holding it can act as the bot anywhere the bot is a member, which is a wider grant than any of the four webhook connectors offer.

  3. WhatsApp uses a bearer token

    Sent as a header against Meta's API, in the conventional place, alongside an identifier for the number that sends. A leak reaches whatever that token was scoped to at Meta.

  4. Both refuse before they call

    An incomplete configuration returns a reason rather than attempting a request with a missing credential, so a half-set-up connector reads as unconfigured rather than as failing.

The one operational habit worth having

Because a webhook cannot be scoped, cannot be audited and never expires, the only real control you have is replacement. It is quick — revoke the old one in the chat tool, create a new one, paste it in — and it is the correct response to a long list of ordinary events that nobody thinks of as security events.

Somebody who set it up leaves. The URL appears in a screenshot in a ticket. A contractor's access is wound down. The value was pasted into a shared document during setup and the document still exists. In every one of those, the webhook is more widely known than it was, and nothing anywhere will tell you so.

Rotating is cheap and there is no state to migrate — the new URL simply starts being used. That asymmetry between the cost of rotating and the cost of not knowing is the argument for doing it on a schedule rather than on suspicion.

What is in place, layer by layer

How chat credentials are handled

Encrypted at rest

Webhook URLs and bot tokens alike are stored through an encrypted cast on the row for a single workspace, never in configuration or environment.

Built in

Kept out of logs

A failed send logs the provider's error message. The destination address is not part of the logged context, so a credential does not end up in a file with a wider audience.

Built in

Kept out of error messages

What a user sees when a send fails is the reason, not the address, so a screenshot of a failure is not a leak.

Built in

A permissioned setting

Configuring a chat destination is its own right rather than something every signed-in member can change, which limits both who can redirect alerts and who has seen the value.

Built in

Refusal before the request

An incomplete configuration returns a reason instead of sending a call with a missing credential, so the failure names the cause.

Built in

One destination per credential

Each of the four webhook connectors is bound to a single channel or space by the provider, so the reach of a leaked value is fixed and small.

Built in

Replaceable in seconds

Rotation is paste-and-save with nothing to migrate, which is what makes it reasonable to do on a schedule rather than only after a scare.

Built in

The provider's rejection surfaced

A revoked webhook and a malformed payload produce different provider responses, and both are shown as given rather than collapsed into one message.

Built in

Signed inbound events are a different connector and a different model — there, a shared secret and a computed digest decide whether a request is genuine. The asymmetry is the provider's, not ours.

Three positions held on purpose

  • A webhook URL is handled as a credential from the moment it is pasted. It does not look like one, which is precisely the argument for encrypting it, keeping it out of logs and gating who can set it.
  • The destination is never included in a log line or an error message. A failure that a user can screenshot, or an operator can grep, should not be a way to obtain the thing that authorises posting.
  • Rotation is presented as routine rather than as an incident response. A credential that cannot expire, cannot be scoped and cannot be audited is one whose only real control is replacement, and treating that as ordinary maintenance is the honest framing.

Five questions about how a product holds your webhook

Is it encrypted at rest?

A good answer sounds like

Yes, per workspace.

What ours actually is

Stored through an encrypted cast on the row for one workspace. Not in configuration, not in environment.

Does it appear in your logs?

A good answer sounds like

No.

What ours actually is

A failed send logs the provider's message. The address is not in the context.

Who can change it?

A good answer sounds like

A named permission.

What ours actually is

Configuring a chat destination is its own right, not open to every member.

What does a leak actually get someone?

A good answer sounds like

A specific, bounded answer.

What ours actually is

The ability to post into one channel. No read access, no other channel, nothing else in the workspace.

How do I rotate it?

A good answer sounds like

Quickly, with no migration.

What ours actually is

Revoke at the provider, create a new one, paste and save. There is no state that carries over.

Our take

Security reviews are good at asking about the things that look like secrets and poor at asking about the things that are. A webhook URL passes through a procurement questionnaire without a comment because there is no question shaped like it — it is not a key, not a password, not an OAuth grant, and the field it lives in is usually labelled URL. Meanwhile it is a permanent, unrevocable-by-you, unauditable permission to put words in front of your team in a place they trust. The good news is that the handling required is entirely ordinary. The only hard part is noticing that it applies.

Treat the link like the key it is

Encrypted, out of the logs, behind a permission, and rotated when the people around it change. None of it is difficult; all of it depends on calling the thing by its real name.

Talk through connector security

Frequently asked questions

Can someone with our webhook URL read our Slack messages?

No. An incoming webhook is write-only and bound to one channel. It grants the ability to post there and nothing else — no reading, no other channels, no workspace access. That is the reason a leak is a nuisance rather than a breach, and it is worth knowing precisely so the response is proportionate.

How would we know if a webhook had leaked?

Realistically, by seeing a message in the channel that your systems did not send. There is no audit trail on the provider side attributing posts to a sender, because there is no sender identity in the protocol. This is the strongest argument for scheduled rotation: you cannot detect the condition, so you reduce how long it can persist.

Should we use a private channel for alerts?

It helps for a different reason — it limits who reads the operational detail in the messages, which is often the more sensitive half. It does nothing about the webhook itself, since the URL works regardless of the channel's privacy.

Is Telegram less safe because the token is in the URL?

It is a broader credential and it sits in a position where things get logged by intermediaries, so it deserves more care than a channel-bound webhook. Whether that matters depends on your infrastructure. The honest summary is that the four webhook connectors have the smallest blast radius of the six, and that is a legitimate reason to prefer one of them.

What happens to queued messages when we rotate the webhook?

Anything already dispatched against the old URL will be refused by the provider and logged with its reason. Nothing is lost from your workspace — chat is a notification channel, and the record it was telling you about is unaffected.

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center