Security
How a push from Slack is checked on a machine that packs agents (When Slack pushes its messages), beyond what every webhook's secret gets (Link's Credentials page). At home the app runs in Socket Mode, and nothing listens.
Pairing the owner
Link opens Socket Mode before printing the one-time code. It takes the code only from a person's direct message, ignoring channel messages, bots and edits, and acknowledges every Socket Mode envelope before handling it. The wait ends on expiry or connection loss. Pairing confirms in Slack before saving the owner's member ID and DM channel.
A shared channel
Only a paired user's mention with the code shares a channel. It expires
after ten minutes and is taken once. Members speak as
person:slack:<workspace>/<user>, never as the owner. Both the socket
bridge and signed pushes check the sender's workspace. A member of
another workspace in Slack Connect, a bot, an edit and a member's direct
message are refused. Unsharing removes the channel from the running
bridge's audience. Messages from members never add a remembered
destination for future answers.
A push
Each push is checked before its body is read, in this order, and refused at the first check it fails.
Scheme
Slack signs each push with the app's signing secret:
X-Slack-Signature: v0=<hex>, an HMAC-SHA256 over
v0:<X-Slack-Request-Timestamp>:<body>. sift checks it through
Link's one way in (link_secret_value::inbound, Scheme::SLACK), the
one HMAC in Link, compared in constant time.
Cap
A push is at most 1 MiB (UPDATE_MAX), checked before anything is
read, and answered 413 over it.
Window
A push's timestamp must be within 300 seconds of now, either side, as Slack advises: a push heard on the way can't be sent again later. One outside it is answered 401.
Answers
sift answers 401 for a signature missing or wrong, or a time outside
the window; 400 for a time missing or not a number; and 503 with no
signing secret, closed, so Slack tries again. Only then is the body
read.
What's read of the body
Slack's check of the address (url_verification) is answered with its
challenge. An event the bridge wouldn't answer (another workspace's, a
stranger's, the bot's own) is dropped with a 200, so Slack doesn't push
it again.
A repeat
A push Slack sends again, or one replayed inside the window, has the
same event_id, which sift returns as the push's dedupe: whatever
runs sift answers each one once, per app. After the window, the
signed time refuses it.
Rolling its signing secret
A person rolls it on the app's page at https://api.slack.com/apps: under
Basic Information > App Credentials > Signing Secret, Regenerate, and
Slack signs every push with the new one from then. Then give the new
one as the app's signing_secret, the one before kept beside it
(previous) until it's dropped.
While both are given
sift takes either while both are given, so a push Slack signed with
the old one, and sends again, is still answered; a push in between the
two steps is refused 401, and Slack tries it again. Once the old one is
dropped it's refused, and a previous secret alone takes nothing. No
restart: each push is checked with the secrets it comes with.