そのWebhook、誰からのリクエストか確認してる?
n8nでChatworkのWebhookを受けて、メッセージに自動応答するBotを動かしている人は増えてきた。Webhook nodeのURLを設定して、Chatwork管理画面に貼って、動いた。やった。
で、そこで終わっていないか。
そのURLに、Chatwork以外の誰かがPOSTしたらどうなるか。答えはシンプルで、Botが普通に反応する。偽のメッセージを送りつければ、Botはそれを本物だと思って処理する。
Chatworkの署名検証の仕組み
ChatworkはWebhookリクエストのヘッダに X-ChatWorkWebhookSignature という署名を付けてくる。HMAC-SHA256で計算されている。
Base64( HMAC-SHA256( Base64decode(token), request_body ) )
-
token: Chatwork管理画面のWebhook設定画面で表示されるBase64エンコード済みの文字列 -
request_body: リクエストのボディ(JSON文字列そのもの)
受け取り側で同じ計算をして、ヘッダの署名と一致するか確かめる。一致しなければ捨てる。GitHubのWebhookやSlackのSigning Secretと同じ方式。
n8nのCode nodeに貼るだけのコード
const crypto = require('crypto');
const WEBHOOK_TOKEN = 'YOUR_WEBHOOK_TOKEN';
const headers = $input.first().json.headers;
const signature = headers['x-chatworkwebhooksignature']
|| headers['X-ChatWorkWebhookSignature']
|| '';
const body = $input.first().json.body;
const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
const expected = crypto
.createHmac('sha256', Buffer.from(WEBHOOK_TOKEN, 'base64'))
.update(bodyStr)
.digest('base64');
if (signature !== expected) {
return []; // 署名不一致 → 空配列でワークフロー停止
}
return [{
json: {
verified: true,
...($input.first().json)
}
}];
ポイント3つ
1. トークンはBase64デコードしてからHMACの鍵にする。 Buffer.from(WEBHOOK_TOKEN, 'base64') がそれ。ここを忘れると永遠に署名が一致しない。
2. ヘッダ名の大文字・小文字を両方拾う。 n8nのバージョンやプロキシの設定で正規化されることがある。
3. 署名不一致は return [] で静かに無視。 攻撃者にエラーメッセージを返す必要はない。
Webhook nodeの設定
- Response Mode: 「Immediately」推奨。署名検証で落としたリクエストにタイムアウトまで待たせない
- Include Headers and Query: 有効にする。デフォルトではヘッダーが後続ノードに渡らないことがある
Kubellの技術者が6言語分の実装を公開している
Chatworkを開発しているKubell社の技術者が、公式に6言語(Ruby, PHP, Python, Go, Java, C#)の実装コードを公開している。開発元がここまで用意しているのに、利用者側が無視するのはもったいない。
「URLが漏れなければ大丈夫じゃない?」
「漏れない保証」はどこにもない。n8nのダッシュボードのスクショをSlackに貼った、URLを含むログが残った。漏れるルートはいくらでもある。
署名検証は保険ではない。リクエストの正当性を担保する唯一の手段だ。
トークンはコードに直書きしない
上のコード例では 'YOUR_WEBHOOK_TOKEN' をそのまま書いているが、本番運用ではn8nのCredential機能やEnvironment Variableに入れる。セキュリティの記事でトークンをハードコードしていたら本末転倒だ。
まとめ
- ChatworkのWebhookには
X-ChatWorkWebhookSignatureヘッダが付いてくる - HMAC-SHA256で検証すれば、不正なリクエストを弾ける
- n8nならCode nodeを1つ追加するだけ。5分で終わる
セキュリティは後回しにしがちだ。でも5分で入れられるなら、入れない理由がない。
Chatworkシリーズ
- #1 なぜ2026年にまだChatworkを使い倒しているのか
- #2 chatwork-client-gas、ぶっちゃけいるの?
- #3 ルームの参加者データだけで、組織の人間関係マップを作った
- #4 「Chatworkに確定連絡が来たら請求書を送る」をGASで自動化する
- #5 Chatwork MCPを繋いだら、17ルームの未読が10秒で片付いた
- #6 MCP vs GAS — Chatwork自動化の「正解」はどっちか
- #7 コンタクト承認をn8nで自動化しようとしたら、3つの罠にハマった
- #8 ChatworkにAIチームを住まわせたら、勝手に会話が始まった
- #9 Chatwork 8ルームの全メッセージからFAQ429件を自動抽出した
- #10 Webhook署名検証を入れたら全メッセージが消えた
- #11 過去メッセージを全件取得しようとしたら、APIの「100件の壁」にハマった
- #12 Chatwork APIの「既読」は自分で制御できる
- #13 Chatwork APIのファイル機能、使ったことある?
- #14 n8nで全ルーム巡回
- #15 タスク機能をAPIで使い倒す
- #16 MCPを2アカウント同時接続したら、仕事用と事務局用が1画面で回った
- #17 【世界初かもしれない】ChatworkでClaude Code Channelsを実装してみた
- #18 Chatwork × Dify × GASで問い合わせ回答を自動提案する
- #19 RelationMapを夜間バッチで毎日自動更新する
- #20 17記事書いて見えた、Chatwork APIエコシステムに足りないもの
- #25 Chatwork × GAS × Claude Codeで会員制講座の運用を自動化した
- #21 Googleフォームの回答をChatworkに自動投稿するGAS
- #22 Chatworkの会話を毎日AIが要約してくれる仕組みをn8nで作った話
- #23 chatwork-cliを入れたら、シェルからChatworkが操作できて世界が変わった
- #24 ChatworkのWebhookをn8nで受けるなら、HMAC署名検証は必ずやれ(この記事)
Chatworkシリーズ
- #1 なぜ2026年にまだChatworkを使い倒しているのか
- #2 chatwork-client-gas、ぶっちゃけいるの?
- #3 ルームの参加者データだけで、組織の人間関係マップを作った
- #4 「Chatworkに確定連絡が来たら請求書を送る」をGASで自動化する
- #5 Chatwork MCPを繋いだら、17ルームの未読が10秒で片付いた
- #6 MCP vs GAS — Chatwork自動化の「正解」はどっちか
- #7 コンタクト承認をn8nで自動化しようとしたら、3つの罠にハマった
- #8 ChatworkにAIチームを住まわせたら、勝手に会話が始まった
- #9 Chatwork 8ルームの全メッセージからFAQ429件を自動抽出した
- #10 Webhook署名検証を入れたら全メッセージが消えた
- #11 過去メッセージを全件取得しようとしたら、APIの「100件の壁」にハマった
- #12 Chatwork APIの「既読」は自分で制御できる
- #13 Chatwork APIのファイル機能、使ったことある?
- #14 n8nで全ルーム巡回
- #15 タスク機能をAPIで使い倒す
- #16 MCPを2アカウント同時接続したら、仕事用と事務局用が1画面で回った
- #17 【世界初かもしれない】ChatworkでClaude Code Channelsを実装してみた
- #18 Chatwork × Dify × GASで問い合わせ回答を自動提案する
- #19 RelationMapを夜間バッチで毎日自動更新する
- #20 17記事書いて見えた、Chatwork APIエコシステムに足りないもの
- #21 Googleフォームの回答をChatworkに自動投稿するGAS
- #22 Chatworkの会話を毎日AIが要約してくれる仕組みをn8nで作った話
- #23 chatwork-cliを入れたら、シェルからChatworkが操作できて世界が変わった
- #24 ChatworkのWebhookをn8nで受けるなら、HMAC署名検証は必ずやれ(この記事)