Slack signs its requests using a secret unique to your app.
With the help of signed secrets, your app can more confidently verify whether requests from Slack are authentic. The signing process is the cooler, younger sibling of verification tokens.
Slack creates a unique string for your app and shares it with you. Verify requests from Slack with confidence by verifying signatures using your signing secret.
On each HTTP request that Slack sends, Slack adds an
X-Slack-Signature HTTP header. The signature is created by combining the signing secret with the body of the request sent using a standard HMAC-SHA256 keyed hash.
The resulting signature is unique to each request and doesn't directly contain any secret information. That keeps your app secure, preventing bad actors from causing mischief.
Signing secrets replace the old verification tokens. Good news: the new signature is used exactly the same way as the deprecated verification token. It's just computed more securely.
Some SDKs perform signature verification automatically, accessible via a drop-in replacement of your signing secret for your old verification token. See the SDK support section for more detail.
Request signing follows this pattern:
Slack uses HTTP requests to notify your app that something has happened. If you're subscribed to the Events API, your app might receive a request when a reacji has been added to a message. If you're the proud owner of a slash command, your app'll be notified when someone uses your command. You might even be notified that your app has been given new resources and permissions.
When Slack sends your app a request, your app must check to make sure it's authentic. You do this by computing a signature. Let's go over the recipe for Slack's signature dish.
Here's an overview of the process to validate a signed request from Slack:
X-Slack-Request-Timestamp header on the HTTP request, and the body of the request.
Concatenate the version number, the timestamp, and the body of the request to form a basestring. Use a colon as the delimiter between the three elements.
v0:123456789:command=/weather&text=94070. The version number right now is always
With the help of HMAC SHA256 implemented in your favorite programming language, hash the above basestring, using the Slack
Signing Secret as the key.
Compare this computed signature to the
X-Slack-Signature header on the request.
Note: This pseudocode example walks through validating a request. Nearly all modern programming languages can perform equivalent calculations with HMAC SHA256. Here's the library for Node, for example.
Grab your Slack Signing Secret, available in the app admin panel under Basic Info.
In this example, the Signing Secret is
8f742231b10e8888abcd99yyyzzz85a5. (Hint: you don't need to convert the Signing Secret to binary. Treat it as a standard UTF-8 string.) While you're at it, extract the raw request body from the request.
Make sure that this request body contains no headers and is not deserialized in any way. Use only the raw request payload.
``` slack_signing_secret = 'MY_SLACK_SIGNING_SECRET' // Set this as an environment variable >>> 8f742231b10e8888abcd99yyyzzz85a5 request_body = request.body() >>> token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c ```
Extract the timestamp header from the request. In our example the timestamp header is
The signature depends on the timestamp to protect against replay attacks. While you're extracting the timestamp, check to make sure that the request occurred recently. In this example, we verify that the timestamp does not differ from local time by more than five minutes.
timestamp = request.headers['X-Slack-Request-Timestamp'] >>> 1531420618 if absolute_value(time.time() - timestamp) > 60 * 5: # The request timestamp is more than five minutes from local time. # It could be a replay attack, so let's ignore it. return
Concatenate the version number, the timestamp, and the request body together, using a colon (
:) as a delimiter.
In this example, we have the following values:
The resulting example basestring is (
sig_basestring = 'v0:' + timestamp + ':' + request_body >>> 'v0:1531420618:token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c'
Hash the resulting string, using the signing secret as a key, and taking the hex digest of the hash.
In this example, we compute a hex digest of
The full signature is formed by prefixing the hex digest with
v0=, to make
my_signature = 'v0=' + hmac.compute_hash_sha256( slack_signing_secret, sig_basestring ).hexdigest() >>> 'v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'
Compare the resulting signature to the header on the request.
For best practice, use an hmac
compare function instead of directly comparing the signatures for equality.
slack_signature = request.headers['X-Slack-Signature'] >>> 'v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503' if hmac.compare(my_signature, slack_signature): # hooray, the request came from Slack! deal_with_request(request)
For a while, you'll see two secrets side-by-side in the app management panel:
One secret is the new Signing Secret, and one is the deprecated verification token. We strongly recommend you only use the Signing Secret from now on.
Regenerate your secret using the "Regenerate" button next to the Signing Secret in the admin panel.
These Slack SDKs, among others, currently support signing secrets:
In each package, using a signed secret is no different than using the old verification token. Set your Signing Secret as an environment variable:
export SLACK_SIGNING_SECRET=abc123. Then, initialize the package with the secret.
For example, a Node app that uses Interactive Messages would create a message listener using the following single line of code:
// Create the adapter using the app's signing secret, read from env const interactions = createMessageAdapter(process.env.SLACK_SIGNING_SECRET);
A Node app that uses the Events API would initialize a listener for events using the following code:
const slackEvents = createSlackEventAdapter(process.env.SLACK_SIGNING_SECRET);
A Python app that uses the Events API would initialize a listener for events using the following code:
SLACK_SIGNING_SECRET = os.environ["SLACK_SIGNING_SECRET"] slack_events = SlackEventAdapter(SLACK_SIGNING_SECRET, "/slack/events")
You can also check out the open-source BotKit to see an implementation of Slack signature verification in the wild.
We'll continue allowing apps to use verification tokens for now. However, we will retire them completely in coming months. We strongly recommend switching to request signing as soon as possible.
An HTTP request contains:
Content-type: application/json; charset=utf-8
Use only this message body, combined with the timestamp header, as the basestring of your signature.
For example, in Python's Flask, use
request.get_data() before accessing any other methods on the request in order to get the raw request payload, without performing JSON deserialization.
Requests from Slack also support authentication through Mutual TLS.
Mutual TLS has the same goal as request signing: it allows your app to verify that an outgoing HTTP request is authentic. Mutual TLS differs from request signing in where and how it occurs. Instead of verifying request signatures in your application code, you configure your TLS-terminating server to authenticate client certificates from Slack.
Here's the Mutual TLS process in more detail:
Configure your TLS-terminating server to request client certificates.
Your TLS-terminating server should be configured to accept client certificates under the DigiCert Global Root CA certificate. Slack's client certificate may not be signed directly by the root CA certificate, but requests from Slack will include any intermediate CA certificates necessary for verification.
Extract either of the following fields in the certificate.
Subject Alternative Name:
DNS:platform-tls-client.slack.com. By RFC 6125, this is the recommended field to extract.
Subject Common Name:
Inject the extracted domain into a header, and forward the request to your application server.
Here's an example header you might add to the request:
X-Client-Certificate-SAN: platform-tls-client.slack.com. Whatever you choose to call your header, check to make sure this header hasn't already been added to the request. Your upstream application server must know that the header was added by your TLS-terminating server as part of the Mutual TLS process.
In your application server, check the header and verify that its value matches the expected domain,
platform-tls-client.slack.com. Do not accept header values with any other domain name, including other subdomains of slack.com.