Skip to content

Commit 933269e

Browse files
Add flag to AwsLambdaReceiver to enable/disable signature verification (#2107)
* Add flag to `AwsLambdaReceiver` to enable/disable signature verification * Address PR comments * Add blurb to `signingSecret` JSDoc saying that the value of it doesn't matter if sig verification is disabled
1 parent 06b0f61 commit 933269e

File tree

2 files changed

+80
-6
lines changed

2 files changed

+80
-6
lines changed

src/receivers/AwsLambdaReceiver.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,42 @@ describe('AwsLambdaReceiver', function () {
542542
);
543543
assert.equal(response.statusCode, 401);
544544
});
545+
546+
it('does not perform signature verification if signature verification flag is set to false', async () => {
547+
const awsReceiver = new AwsLambdaReceiver({
548+
signingSecret: '',
549+
signatureVerification: false,
550+
logger: noopLogger,
551+
});
552+
const handler = awsReceiver.toHandler();
553+
const awsEvent = {
554+
resource: '/slack/events',
555+
path: '/slack/events',
556+
httpMethod: 'POST',
557+
headers: {
558+
Accept: 'application/json,*/*',
559+
'Content-Type': 'application/json',
560+
Host: 'xxx.execute-api.ap-northeast-1.amazonaws.com',
561+
'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)',
562+
'X-Slack-Request-Timestamp': 'far back dude',
563+
'X-Slack-Signature': 'very much invalid',
564+
},
565+
multiValueHeaders: {},
566+
queryStringParameters: null,
567+
multiValueQueryStringParameters: null,
568+
pathParameters: null,
569+
stageVariables: null,
570+
requestContext: {},
571+
body: urlVerificationBody,
572+
isBase64Encoded: false,
573+
};
574+
const response = await handler(
575+
awsEvent,
576+
{},
577+
(_error, _result) => {},
578+
);
579+
assert.equal(response.statusCode, 200);
580+
});
545581
});
546582

547583
// Composable overrides

src/receivers/AwsLambdaReceiver.ts

+44-6
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,41 @@ export interface AwsResponse {
4040
export type AwsHandler = (event: AwsEvent, context: any, callback: AwsCallback) => Promise<AwsResponse>;
4141

4242
export interface AwsLambdaReceiverOptions {
43+
/**
44+
* The Slack Signing secret to be used as an input to signature verification to ensure that requests are coming from
45+
* Slack.
46+
*
47+
* If the {@link signatureVerification} flag is set to `false`, this can be set to any value as signature verification
48+
* using this secret will not be performed.
49+
*
50+
* @see {@link https://api.slack.com/authentication/verifying-requests-from-slack#about} for details about signing secrets
51+
*/
4352
signingSecret: string;
53+
/**
54+
* The {@link Logger} for the receiver
55+
*
56+
* @default ConsoleLogger
57+
*/
4458
logger?: Logger;
59+
/**
60+
* The {@link LogLevel} to be used for the logger.
61+
*
62+
* @default LogLevel.INFO
63+
*/
4564
logLevel?: LogLevel;
65+
/**
66+
* Flag that determines whether Bolt should {@link https://api.slack.com/authentication/verifying-requests-from-slack|verify Slack's signature on incoming requests}.
67+
*
68+
* @default true
69+
*/
70+
signatureVerification?: boolean;
71+
/**
72+
* Optional `function` that can extract custom properties from an incoming receiver event
73+
* @param request The API Gateway event {@link AwsEvent}
74+
* @returns An object containing custom properties
75+
*
76+
* @default noop
77+
*/
4678
customPropertiesExtractor?: (request: AwsEvent) => StringIndexed;
4779
}
4880

@@ -59,16 +91,20 @@ export default class AwsLambdaReceiver implements Receiver {
5991

6092
private logger: Logger;
6193

94+
private signatureVerification: boolean;
95+
6296
private customPropertiesExtractor: (request: AwsEvent) => StringIndexed;
6397

6498
public constructor({
6599
signingSecret,
66100
logger = undefined,
67101
logLevel = LogLevel.INFO,
102+
signatureVerification = true,
68103
customPropertiesExtractor = (_) => ({}),
69104
}: AwsLambdaReceiverOptions) {
70105
// Initialize instance variables, substituting defaults for each value
71106
this.signingSecret = signingSecret;
107+
this.signatureVerification = signatureVerification;
72108
this.logger = logger ??
73109
(() => {
74110
const defaultLogger = new ConsoleLogger();
@@ -130,12 +166,14 @@ export default class AwsLambdaReceiver implements Receiver {
130166
return Promise.resolve({ statusCode: 200, body: '' });
131167
}
132168

133-
// request signature verification
134-
const signature = this.getHeaderValue(awsEvent.headers, 'X-Slack-Signature') as string;
135-
const ts = Number(this.getHeaderValue(awsEvent.headers, 'X-Slack-Request-Timestamp'));
136-
if (!this.isValidRequestSignature(this.signingSecret, rawBody, signature, ts)) {
137-
this.logger.info(`Invalid request signature detected (X-Slack-Signature: ${signature}, X-Slack-Request-Timestamp: ${ts})`);
138-
return Promise.resolve({ statusCode: 401, body: '' });
169+
if (this.signatureVerification) {
170+
// request signature verification
171+
const signature = this.getHeaderValue(awsEvent.headers, 'X-Slack-Signature') as string;
172+
const ts = Number(this.getHeaderValue(awsEvent.headers, 'X-Slack-Request-Timestamp'));
173+
if (!this.isValidRequestSignature(this.signingSecret, rawBody, signature, ts)) {
174+
this.logger.info(`Invalid request signature detected (X-Slack-Signature: ${signature}, X-Slack-Request-Timestamp: ${ts})`);
175+
return Promise.resolve({ statusCode: 401, body: '' });
176+
}
139177
}
140178

141179
// url_verification (Events API)

0 commit comments

Comments
 (0)