なぜWebhook署名検証が必要なのか
LINE Messaging APIを使ったBotやMini Appでは、LINEプラットフォームからWebhookでイベントが送信されます。しかし、Webhookエンドポイントが公開URLである以上、第三者からの偽装リクエストを受け取る可能性があります。
署名検証を行わない場合のリスク:
- なりすまし攻撃 — 攻撃者がLINEを装って偽のイベントを送信
- データの改ざん — リクエストの内容が途中で変更される
- 不正な操作 — 偽のメッセージイベントでBotに意図しない動作をさせる
署名検証の仕組み
LINEは各WebhookリクエストにX-Line-Signatureヘッダーを付与します。このヘッダーは、チャネルシークレットを鍵としてリクエストボディのHMAC-SHA256ダイジェストをBase64エンコードしたものです。
正しい実装例
import crypto from 'crypto';
function validateSignature(body, signature, channelSecret) {
const hash = crypto
.createHmac('SHA256', channelSecret)
.update(body)
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(hash),
Buffer.from(signature)
);
}
// Express.jsでの使用例
app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
const signature = req.headers['x-line-signature'];
if (!validateSignature(req.body, signature, CHANNEL_SECRET)) {
return res.status(401).send('Invalid signature');
}
// 署名が有効 — イベントを処理
handleEvents(JSON.parse(req.body));
res.status(200).send('OK');
});
よくある間違い
1. bodyをパースしてから検証する
// 間違い — パース済みのbodyでは正しいハッシュが計算できない
app.post('/webhook', express.json(), (req, res) => {
validateSignature(JSON.stringify(req.body), ...); // NG
});
生のリクエストボディ(Buffer)で検証する必要があります。
2. 単純な文字列比較を使う
// 間違い — タイミング攻撃に脆弱
if (hash === signature) { ... }
crypto.timingSafeEqualを使って一定時間比較を行ってください。
3. チャネルシークレットをハードコードする
環境変数で管理し、ソースコードに含めないでください。
WebMoriのLINE API監査
WebMoriのLINE API監査では、以下を自動チェックします:
- X-Line-Signature検証の実装有無
- チャネルアクセストークンの管理方法
- LIFF URLのHTTPS設定
- レート制限(429レスポンス)のハンドリング
- userIdの安全な保存方法
LINE Botを運用している方は、ぜひ一度無料診断をお試しください。