@@ -86,8 +86,11 @@ const formatSlackMessage = (query, response) => {
8686
8787// [START functions_verify_webhook]
8888/**
89- * Verify that the webhook request came from Slack.
89+ * Verify that the webhook request came from Slack by validating its signature .
9090 *
91+ * This function follows the official Slack verification process:
92+ * https://api.slack.com/authentication/verifying-requests-from-slack
93+ *
9194 * @param {object } req Cloud Function request object.
9295 * @param {string } req.headers Headers Slack SDK uses to authenticate request.
9396 * @param {string } req.rawBody Raw body of webhook request to check signature against.
@@ -98,28 +101,34 @@ const verifyWebhook = req => {
98101 const requestTimestamp = req . headers [ 'x-slack-request-timestamp' ] ;
99102
100103 if ( ! requestSignature || ! requestTimestamp ) {
101- throw new Error ( 'Missing slack signature or timestamp headers' ) ;
104+ throw new Error ( 'Missing Slack signature or timestamp headers' ) ;
102105 }
103106
104- // Prevent repeat seizures ( 5 minutes tolerance )
107+ // Protect against replay sttacks by ensuring the request is recent (within 5 minutes)
105108 const fiveMinutesAgo = Math . floor ( Date . now ( ) / 1000 ) - 300 ;
106- if ( parseInt ( requestTime , 10 ) < fiveMinutesAgo ) {
107- throw new Error ( 'Requested slack timestamp is too old' ) ;
109+ if ( parseInt ( requestTimestamp , 10 ) < fiveMinutesAgo ) {
110+ throw new Error ( 'Slack request timestamp is too old' ) ;
108111 }
109112
113+ // Create the base string as Slack expects: version + ':' timestamp + ':' + raw body
110114 const basestring = `v0:${ requestTimestamp } :${ req . rawBody } ` ;
115+
116+ // Create a HMAC SHA256 hash using the Slack signing secret
111117 const hmac = crypto . createHmac ( 'sha256' , slackSigningSecret ) ;
112118 hmac . update ( basestring , 'utf-8' ) ;
113119 const digest = `v0=${ hmac . digest ( 'hex' ) } ` ;
114120
115- if ( ! crypto . timingSafeEqual ( Buffer . from ( digest , 'utf-8' ) , Buffer . from ( requestSignature , 'utf8' ) ) ) {
116- const error = new Error ( 'slack invalid signature' ) ;
121+ // Perform a constant-time comparison to prevent timing attacks
122+ if (
123+ ! crypto . timingSafeEqual (
124+ Buffer . from ( digest , 'utf-8' ) ,
125+ Buffer . from ( requestSignature , 'utf8' )
126+ )
127+ ) {
128+ const error = new Error ( 'Invalid Slack signature' ) ;
117129 error . code = 401 ;
118130 throw error ;
119131 }
120-
121- // This method throws an exception if an incoming request is invalid.
122- verifyRequestSignature ( signature ) ;
123132} ;
124133// [END functions_verify_webhook]
125134
0 commit comments