火曜日, 7月 8, 2025
火曜日, 7月 8, 2025
- Advertisment -
ホームニューステックニュースCloudflare WorkersでSlack Botの署名検証を実装する #slack-api - Qiita

Cloudflare WorkersでSlack Botの署名検証を実装する #slack-api – Qiita



Cloudflare WorkersでSlack Botの署名検証を実装する #slack-api - Qiita

はじめに

前回の記事では、Cloudflare Workersを使ったSlack Bot(技術記事下書きBot)の実装方法を紹介しました。その中で触れた署名検証について実装していきます。

なぜ署名検証が必要なのか

前回作成したBotのように、公開エンドポイントでSlackからのリクエストを受け取る場合、そのURLが第三者に知られると、偽のリクエストを送信される可能性があります。

このリスクを防ぐために、Slackの署名検証機能を実装する必要があります。

署名検証の仕組み

Slackの署名検証は以下の流れで動作します

  1. SlackがリクエストボディとタイムスタンプをHMAC-SHA256で署名
  2. 署名をリクエストヘッダー(x-slack-signature)に含めて送信
  3. Bot側で同じ方法で署名を計算し、一致するか確認

検証は以下の流れで行います

  1. (事前準備)Slack API設定画面から、Signing Secretを取得する
  2. リクエストからタイムスタンプを取得する
    リプレイ攻撃から保護するために、公式の例に則って検証時刻とタイムスタンプのずれが5分以上あれば以降の処理を中断します
  3. {バージョン番号}:{タイムスタンプ}:{リクエスト本文}の形式で連結する
  4. 署名シークレットをキーとして使用して連結文字列をハッシュし、ダイジェストを取得する
  5. 結果をリクエストヘッダーと比較する
    タイミング攻撃から保護するため、timingSafeEqualを利用して比較します

詳しくは公式ドキュメントを参照してください

実装

署名検証処理

import crypto from 'node:crypto';
import { Buffer } from 'node:buffer';

/**
 * Slackのリクエストを検証
 * @param headers リクエストヘッダー
 * @param body リクエストボディ
 * @param signingSecret Slackのシグネチャ
 * @returns リクエストが有効かどうか
 */
function isValidSlackRequest(headers: Headers, body: string, signingSecret: string): boolean {
	// 1. 必須ヘッダーの存在確認
	const timestamp = headers.get('x-slack-request-timestamp');
	const signature = headers.get('x-slack-signature');

	if (!timestamp || !signature) {
		console.warn('Missing required Slack headers');
		return false;
	}

	// 2. タイムスタンプ範囲検証(5分以内)
	const timestampNum = parseInt(timestamp, 10);
	const now = Math.floor(Date.now() / 1000);
	const timeDiff = Math.abs(now - timestampNum);
	if (timeDiff > 60 * 5) {
		console.warn('Request timestamp too old or future', { timeDiff });
		return false;
	}

	// 3. 署名形式の事前チェック
	if (!signature.startsWith('v0=') || signature.length !== 67) {
		// v0= + 64文字のhex
		console.warn('Invalid signature format');
		return false;
	}

	// 4. 署名検証
	const baseString = `v0:${timestamp}:${body}`;
	const computedSignature = 'v0=' + crypto.createHmac('sha256', signingSecret).update(baseString).digest('hex');

	// 5. タイミングセーフ比較
	try {
		return crypto.timingSafeEqual(Buffer.from(computedSignature), Buffer.from(signature));
	} catch (error) {
		console.warn('Signature comparison failed', error);
		return false;
	}
}

利用側

/**
 * handler
 */
export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext): PromiseResponse> {
		const rawBody = await request.text(); // 追加

		// 今回追加した署名検証
		if (!isValidSlackRequest(request.headers, rawBody, env.SLACK_SIGNING_SECRET)) {
			console.warn('Unauthorized – bad Slack signature', {
				timestamp: request.headers.get('x-slack-request-timestamp'),
				signature: request.headers.get('x-slack-signature'),
			});
			return new Response('Unauthorized – bad Slack signature', { status: 401 });
		}

		const url = new URL(request.url);

		if (request.method === 'POST' && url.pathname === '/slack/events') {
            // const rawBody = await request.text(); // 冒頭に移動
			const params = new URLSearchParams(rawBody);
			const responseUrl = params.get('response_url') ?? '';
			const text = params.get('text') ?? '';

			// 非同期バックグラウンド実行
			ctx.waitUntil(handleSlackEvent(text, responseUrl, env));
			// 即時レスポンス(Slackタイムアウト回避)
			return new Response(`✏️プロンプトを受け取りました。記事生成中です。\nprompt:\n${text}`);
		}

		return new Response('Not Found', { status: 404 });
	},
} satisfies ExportedHandlerEnv>;

追加設定

wrangler.jsonc

	"compatibility_flags": ["nodejs_compat"],
	"compatibility_date": "2024-09-23", // 元の設定値を書き換える

Node.jsランタイムAPIのcryptoを使うため必要な設定です。
詳しくは公式ドキュメントを参照してください。

環境変数

型の追加

interface Env {
	OPENAI_API_KEY: string;
	GITHUB_TOKEN: string;
	GITHUB_REPO: string;
	GITHUB_BRANCH: string;
	SLACK_SIGNING_SECRET: string; // 追加
}

ターミナルで以下を実行し、環境変数を設定します

npx wrangler secret put SLACK_SIGNING_SECRET

入力欄表示後、SlackAPI設定画面より取得したSigning Secretを入力します。
設定後、Cloudflare Workersの設定画面から項目が確認できるようになっていればOKです。

デプロイ

前回記事同様、以下のコマンドでデプロイできます。

まとめ

前回記事に続き、SlackBotをCloudflareWorkers環境にデプロイする際必要な署名検証機能を追加しました。

これにより安全で実用的なBotになります。

参考





Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -