Verify slack request signature

  1. My goal: Integrate Slack with Retool workflow webhook
  2. Issue: Is it possible to very slack request signature as per slack documentation verifying-requests-from-slack?
  3. Additional info: self hosted onprem 3.196.2

But I think I need the rawbody of a request and this could lead to this topic RawBody from Webhook is it possible to access the raw body?

OP of the thread you linked. The not as secure workaround is to use the verification token and timestamp to authenticate the request.

Is there a working solution shared by any user?

I'm too lazy to figure out how to share a workflow. But the logic is just that you take the verification token and check it. Also check for the time to be less than X seconds to defeat replay attacks.

Or if you don't send files over the message you can carefully rebuild the rawBody and verify it then. This works because the ordering is constant when it becomes a data object :). This fails for files because the bytes content is encoded differently and I'm too lazy to do the case work of unencoding it and reencoding it etc.

Welcome to the community, @dda! Just confirming that I've connected this topic to the one you referenced previously and will update both as soon as there's been movement towards implementing this within Retool. :+1:

Thank you all!

It is ok I will test the old token validation system and timestamp, probably whitelisting IPs if is possible. But, the signature verification system (or HMAC I think) is new standard for many vendors so I think is pretty important. I tried to rebuild the rawBody quickly but I failed for now. I will test the token as intermediate solution. Proxying the requests exposing another api capable to verify the signature sounds time consuming and need to expose another app publicly..

1 Like

+1 we also need this for many of our Retools and haven't found a good solution yet. I can try the old token validation out too. But verifying raw body will be crucial for other webhooks too (e.g. Twilio).

1 Like

Welcome to the community, @ankurkwv! I've updated our internal feature request with this additional context and will provide an update as soon as I have news to share. :+1:

Great news!! at least for onPrem there is a solution as explained in a linked post RawBody from Webhook - #9 by Darren Thank you a lot!

1 Like

Thanks for sharing this update, @dda!

If anybody is interested and want to review the solution with 2 simple code blocks (could be just one).

  1. get the raw body and decode it
const { Buffer } = require('buffer');

const decodedBody = Buffer.from(startTrigger.metadata.rawDataBase64, 'base64').toString('utf-8');

return decodedBody;
  1. Verify the signature
const crypto = require('crypto');

function verifySlackRequest(signingSecret, headers, body) {
  const slackSignature = headers['x-slack-signature'];
  const slackTimestamp = headers['x-slack-request-timestamp'];
  
  const currentTimestamp = Math.floor(Date.now() / 1000); // Convert to integer seconds
  const timeDifference = Math.abs(currentTimestamp - slackTimestamp);
  if (timeDifference > 300) { 
    console.log('Request timestamp is too old.', timeDifference);
    return false;
  }

  const basestring = `v0:${slackTimestamp}:${body}`;

  const computedSignature = `v0=${crypto
    .createHmac('sha256', signingSecret)
    .update(basestring, 'utf8')
    .digest('hex')}`;

  const isValid = crypto.timingSafeEqual(
    Buffer.from(computedSignature, 'utf8'),
    Buffer.from(slackSignature, 'utf8')
  );

  return isValid;
}

const slackSigningSecret = retoolContext.configVars.slack_retool_bot_signing_secret;
const decodedRequestBody = decode_request_raw_body.data;
const requestHeaders = startTrigger.headers

return verifySlackRequest(slackSigningSecret, requestHeaders, decodedRequestBody)