プロフィール

arismmn timeline blog

← ブログ一覧に戻る

Slack認証情報の整理と使い分け完全ガイド

SlackセキュリティAPICloudflare

📝 はじめに

複数のプロジェクトでSlack連携を実装していると、いつの間にか「あれ、このトークンは何に使うんだっけ?」「WebhookとBotの違いって?」と混乱してしまうことがあります。

この記事では、Slack連携で使用する認証情報を通信の方向という観点から整理し、それぞれの役割と使い分けを解説します。

🎯 通信方向で理解するSlack認証

Slack連携の認証情報は、データの流れる方向によって必要なものが変わります。

📊 認証情報マップ

通信の方向 必要な認証情報 用途 使用場面
🚀 Slack送信 SLACK_BOT_TOKEN Slack API呼び出し認証 エラー通知、イベント報告
🔒 Slackから受信 SLACK_SIGNING_SECRET Slackからのリクエスト署名検証 Events API、ボタン操作
🌐 外部から受信 WEBHOOK_SECRET カスタムWebhook認証 Discord等からの投稿
📍 送信先指定 SLACK_*_CHANNEL 通知先チャンネルID どこに送るか指定

この表を頭に入れておくと、「今実装しようとしている機能は、どの方向の通信なのか?」を考えるだけで、必要な認証情報が分かります。

🔑 認証情報の詳細解説

1. SLACK_BOT_TOKEN - Slackへ送信する鍵

これは何?

  • Slack APIを呼び出すための認証トークン
  • xoxb- で始まる文字列(例: xoxb-123456-789012-abcdefg

どこで取得?

  1. Slack App設定ページを開く
  2. 「OAuth & Permissions」セクション
  3. 「Bot User OAuth Token」をコピー

どんな時に使う?

// Slackにメッセージを送信する例
await fetch('https://slack.com/api/chat.postMessage', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${env.SLACK_BOT_TOKEN}` // ← ここで使用
  },
  body: JSON.stringify({
    channel: 'C0XXXXX1234', // 実際のチャンネルID
    text: '✨ お嬢様、処理が完了いたしました!'
  })
});

セキュリティのポイント:

  • ✅ 必ずSecretとして暗号化保存
  • ✅ GitHubなどに直接コミットしない
  • Cloudflare Dashboardの「Encrypt」チェックを有効化

2. SLACK_SIGNING_SECRET - Slackからの通信を検証する鍵

これは何?

  • Slackから届いたリクエストが本物かどうか確認するためのシークレット
  • ランダムな文字列

どこで取得?

  1. Slack App設定ページを開く
  2. 「Basic Information」セクション
  3. 「Signing Secret」をコピー

どんな時に使う?

  • SlackのEvents APIでイベントを受信する時
  • Interactive Components(ボタンやメニュー)からの操作を受信する時
  • Slash Commandsを受信する時

検証の仕組み:

// Slackからのリクエストを検証する処理
async function verifySlackSignature(request, signingSecret, rawBody) {
  const timestamp = request.headers.get('X-Slack-Request-Timestamp');
  const signature = request.headers.get('X-Slack-Signature');
  
  // 1. タイムスタンプが5分以内か確認(リプレイ攻撃対策)
  const currentTime = Math.floor(Date.now() / 1000);
  if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
    return false;
  }
  
  // 2. HMAC-SHA256で署名を計算
  const sigBasestring = `v0:${timestamp}:${rawBody}`;
  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    'raw',
    encoder.encode(signingSecret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );
  const hmac = await crypto.subtle.sign('HMAC', key, encoder.encode(sigBasestring));
  const computedSignature = 'v0=' + Array.from(new Uint8Array(hmac))
    .map(b => b.toString(16).padStart(2, '0')).join('');
  
  // 3. Slackが送信した署名と一致するか確認
  return signature === computedSignature;
}

なぜ必要?

  • 悪意のある第三者が「なりすまし」でリクエストを送ってくるのを防ぐ
  • Slackだけが知っているシークレットで署名することで、本物のSlackからのリクエストだと証明できる

セキュリティのポイント:

  • ✅ 必ずSecretとして暗号化保存
  • ✅ 検証に失敗したリクエストは即座に拒否する
  • ✅ タイムスタンプチェックでリプレイ攻撃を防ぐ

3. WEBHOOK_SECRET - 外部サービスからの通信を検証する鍵

これは何?

  • あなたのシステムが独自に定義したWebhook用の認証トークン
  • Slackとは無関係(Discord、GitHub、独自システム等からのWebhook用)

どこで取得?

  • 自分で生成する(例: openssl rand -hex 32

どんな時に使う?

// Discord Webhookからの投稿を受け取る例
export const POST: APIRoute = async ({ request, locals }) => {
  const runtime = (locals as any).runtime;
  
  // Authorization ヘッダーでトークンを検証
  const authHeader = request.headers.get('Authorization');
  const expectedToken = runtime.env.WEBHOOK_SECRET;
  
  if (!authHeader || authHeader !== `Bearer ${expectedToken}`) {
    return new Response("Unauthorized", { status: 401 }); // ← 拒否
  }
  
  // 検証OKなら処理を続行
  const body = await request.json();
  // ... 投稿を保存する処理 ...
};

Slackのものと何が違う?

SLACK_SIGNING_SECRET WEBHOOK_SECRET
誰が発行? Slackが生成 自分で生成
検証方法 HMAC-SHA256署名 Bearerトークン比較
用途 Slackからのイベント検証 外部サービスからのWebhook検証

セキュリティのポイント:

  • ✅ 必ずSecretとして暗号化保存
  • ✅ 長い(32バイト以上)ランダムな文字列を使用
  • ✅ 定期的に更新する運用を検討

4. SLACK_*_CHANNEL - 通知先を指定する

これは何?

  • Slackチャンネルの一意なID
  • C で始まる11桁の文字列(例: C0XXXXX1234

どこで取得?

  1. Slackでチャンネル名を右クリック
  2. 「リンクをコピー」を選択
  3. URLの末尾 /C0XXXXX1234 の部分を抽出

種類と用途:

環境変数名 用途 通知内容例
SLACK_ERROR_CHANNEL エラー通知 DB接続エラー、API呼び出し失敗
SLACK_AUTH_CHANNEL 認証イベント通知 パスキー登録成功、ログイン失敗
SLACK_REPORT_CHANNEL イベント進捗報告 目標ポイント達成、目標値変更通知

使用例:

const notifyError = async (message, env) => {
  await fetch('https://slack.com/api/chat.postMessage', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${env.SLACK_BOT_TOKEN}`
    },
    body: JSON.stringify({
      channel: env.SLACK_ERROR_CHANNEL, // ← チャンネルIDを指定
      text: `🚨 エラー: ${message}`
    })
  });
};

セキュリティのポイント:

  • ✅ 平文で wrangler.jsonc に保存してOK
  • ✅ チャンネルIDだけでは投稿も閲覧もできない(ワークスペースのメンバーかつ招待済みである必要がある)
  • ✅ むしろ平文にすることで設定ミスを防げる

🏗️ 実践: プロジェクト別の使い分け

私のプロジェクトでは、以下のように使い分けています。

プロジェクト構成

~/projects/
├── event-worker/       # 軍師bot(イベント管理Worker)
├── webhook-worker/     # メイドbot(つぶやき記録Worker)
└── webapp/             # 管理画面(Astro + Cloudflare Pages)

使用マトリクス

認証情報 event-worker webhook-worker webapp
SLACK_BOT_TOKEN
SLACK_SIGNING_SECRET
WEBHOOK_SECRET
SLACK_ERROR_CHANNEL
SLACK_AUTH_CHANNEL
SLACK_REPORT_CHANNEL

なぜこの構成?

event-worker と webhook-worker(Workers):

  • Slackから直接イベントを受信 → SLACK_SIGNING_SECRET が必要
  • Slackへ通知を送信 → SLACK_BOT_TOKEN が必要
  • チャンネルIDは固定(コード内に直接記述)

webapp(Pages):

  • ユーザー操作や外部Webhookを受信 → WEBHOOK_SECRET が必要
  • Slackへ通知を送信 → SLACK_BOT_TOKEN が必要
  • Slackから直接イベントは受けない → SLACK_SIGNING_SECRET は不要
  • 複数チャンネルに通知 → SLACK_*_CHANNEL で柔軟に切り替え

🎭 設計思想: Bot の役割分担

このプロジェクトでは、2つの異なるBotを使い分けています。

軍師bot(event-worker)

役割: イベント進捗管理・戦略立案

担当機能:

  • スクショからイベントポイント読み取り
  • 日課達成の判定と祝電送信提案
  • 進軍予定表の作成
  • 戦術ごとの必要リソース計算
  • 推奨戦術の提案
  • 目標値変更の通知

キャラクター: 忠実で知的な軍師 / 口調「〜ですぞ」「〜でございます」

メイドbot(webhook-worker + webapp)

役割: お屋敷(システム)の管理・記録

担当機能:

  • つぶやきの記録
  • 認証イベントの通知
  • エラー監視と報告

キャラクター: 執事のような丁寧なメイド / 口調「〜ですわ」「〜でございます」

なぜ分ける?

  • 通知の意図が明確になる

    • 「軍師から通知 → イベント関連」「メイドから通知 → システム管理関連」と一目で分かる
  • 機能の拡張がしやすい

    • 軍師botに新しい戦略支援機能を追加しても、メイドbotには影響しない
  • トークンの使い分けが明確

    • 管理画面(webapp)で目標値を変更した場合も、「イベント関連」なので軍師botのトークンで通知
    • エラー発生時は「システム管理」なのでメイドbotのトークンで通知

💡 よくある質問と答え

Q1. 管理画面(webapp)から軍師botのトークンを使うのはなぜ?

A: イベント関連の通知はすべて「軍師の担当」という設計のため。管理画面でユーザーが目標値を変更した際も、Slackには軍師として報告するのが自然です。

// webapp/src/pages/admin/admin-dashboard.astro より
const token = runtime.env.EVENT_WORKER_BOT_TOKEN; // ← 軍師botのトークン
await fetch('https://slack.com/api/chat.postMessage', {
  headers: { 'Authorization': `Bearer ${token}` },
  body: JSON.stringify({
    channel: runtime.env.SLACK_REPORT_CHANNEL,
    text: '⚔️ *軍師の進言* ⚔️\n目標値が変更されました…'
  })
});

Q2. event-worker Worker と webapp Pages は別のプロジェクトなのに、同じトークンを使っていい?

A: むしろ推奨です。同じSlack Appのトークンを共有することで、「軍師bot」として一貫したアイデンティティで通知できます。

Cloudflareの環境変数はプロジェクトごとに独立しているため、以下のように両方に設定します:

# event-worker Worker
npx wrangler secret put SLACK_BOT_TOKEN

# webapp Pages
# Cloudflare Dashboard から EVENT_WORKER_BOT_TOKEN として同じ値を登録

Q3. Workerには SLACK_SIGNING_SECRET があるのに、Pagesにはないのはなぜ?

A: Workers(event-worker/webhook-worker)はSlackから直接イベントを受信するので Signing Secret が必要です。Pagesはユーザー操作や外部Webhookを受信するのみで、Slackからの直接イベントは受けないため不要です。

プロジェクト Slackからイベント受信 SLACK_SIGNING_SECRET
event-worker (Worker) ✅ する ✅ 必要
webhook-worker (Worker) ✅ する ✅ 必要
webapp (Pages) ❌ しない ❌ 不要

Q4. チャンネルIDを平文で wrangler.jsonc に書いて大丈夫?

A: 問題ありません。チャンネルIDだけでは投稿も閲覧もできません(ワークスペースのメンバーかつ招待済みである必要があります)。

むしろ平文にすることで以下のメリットがあります:

  • ✅ 設定ミスに気づきやすい(typoしたらすぐわかる)
  • ✅ GitHubでレビューしやすい(チャンネルIDの変更履歴が残る)
  • ✅ Dashboardで確認しやすい(どのチャンネルに通知しているか一目瞭然)
// wrangler.jsonc
{
  "vars": {
    "SLACK_ERROR_CHANNEL": "C0XXXXX1234",    // #errors
    "SLACK_AUTH_CHANNEL": "C0XXXXX5678",     // #auth-events
    "SLACK_REPORT_CHANNEL": "C0XXXXX9012"    // #event
  }
}

📋 チェックリスト: 新しいSlack連携を追加する時

新しい機能でSlack連携を実装する際は、以下のチェックリストを確認しましょう。

1. 通信の方向を確認

  • Slackへ送信する? → SLACK_BOT_TOKEN が必要
  • Slackから受信する? → SLACK_SIGNING_SECRET が必要
  • 外部から受信する? → WEBHOOK_SECRET が必要

2. 認証情報の取得と設定

  • 必要なトークン/シークレットを取得した
  • Cloudflare Dashboardで環境変数を登録した
  • "Encrypt" チェックを有効化した(トークン/シークレットの場合)
  • wrangler.jsonc にチャンネルIDを追加した(必要な場合)

3. セキュリティ対策

  • トークンをコードに直接書いていない
  • GitHubのsecretとして登録した(CI/CD使用時)
  • Slackからのリクエストは署名検証している
  • ユーザー入力はサニタイズしている

4. テスト

  • ローカルで動作確認した(wrangler dev
  • 本番環境で動作確認した
  • エラーハンドリングを実装した
  • Slack通知が正しいチャンネルに届いた

🎓 まとめ

Slack連携の認証情報は、通信の方向で考えると整理しやすくなります。

方向 認証情報 保存形式
🚀 送信 SLACK_BOT_TOKEN Secret
🔒 受信(Slack) SLACK_SIGNING_SECRET Secret
🌐 受信(外部) WEBHOOK_SECRET Secret
📍 送信先 SLACK_*_CHANNEL 平文

覚えておくべき3つのポイント:

  1. トークンは送信用、シークレットは検証用
  2. Workerは署名検証が必要、Pagesは不要(直接受信しないため)
  3. Bot の役割を明確にすると、トークンの使い分けが自然に決まる

この整理により、「あれ、どのトークンを使うんだっけ?」という迷いがなくなり、自信を持ってSlack連携を実装できるようになります。


関連記事:

更新履歴:

  • 2026-02-21: 初版作成
Gemini
Powered by
Gemini
(使用モデル Gemini3)
← ブログ一覧に戻る