1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DifyでLINE Botの署名検証が厳しそうだった話

Last updated at Posted at 2025-12-25

DifyでリプライメッセージをするLINE Botを作ってみましたが、これは署名検証がないのでなりすましも出来てしまいます。

署名検証

1. ユーザーがLINEにメッセージ
2. LINEのサーバー
3. 自分のアプリでデータを受ける
4. (検証) ← 本来は必要
5. リプライメッセージのAPIにリクエスト

2 => 3の部分でLINEのサーバーと自分アプリでデータが送られてきますが、5でリプライをする前の4でデータがしっかりLINEのサーバーから送られてきてるかを検証する必要があります。

Webhookトリガーで受ける際に

  • ヘッダーに入っているX-Line-Signature
  • 生データ文字列
  • チャンネルシークレット

この3つを使って検証します。

Difyだと署名検証が出来なさそうだった

X-Line-Signatureは抜き出せますし、チャンネルシークレットも問題ないですが...

CleanShot 2025-12-25 at 14.32.51.png

生データ文字列が厳しそうでした。

DifyのWebhookトリガーはデフォルトで_webhook_rawという変数にリクエストボディを Objectにしたデータ を送る形式になっていました。

結構試してたのですがうまくいかず。悩んでましたが厳しそう。

LINEのドキュメントによるとObjectにしたデータは厳しそう

こちらにあるように、LINEから送られてきたリクエストボディは 文字列でそのまま検証しないといけません。

CleanShot 2025-12-25 at 15.35.38.png

CleanShot 2025-12-25 at 15.36.24.png

なのに、Difyはオブジェクトで返してきます。

CleanShot 2025-12-25 at 16.17.34.png

出力変数がobjectで固定となっているので、Webhookトリガーの中で処理が走ってしまってそうです。

コンテンツタイプ text/plainも厳しそう

コンテンツタイプの選択肢をtext/plainにしたら生データが送られてきそうな雰囲気はありますが、こちらに指定するとLINEからのメッセージが到達しませんでした。

CleanShot 2025-12-25 at 15.37.23.png

別の検証サンプル

一応Node.jsで以下のコードで"生データ文字列"にて検証は出来ます。

const crypto = require("node:crypto");
const http = require("node:http");

const HOST = "api.line.me";
const REPLY_PATH = "/v2/bot/message/reply";
const CH_SECRET = process.env.CH_SECRET;
const CH_ACCESS_TOKEN = process.env.CH_ACCESS_TOKEN;
const PORT = 3000; //PORT

const httpClient = async (replyToken, SendMessageObject) => {
  const REPLY_API_ENDPOINT = `https://${HOST}${REPLY_PATH}`;
  const postDataStr = JSON.stringify({ replyToken, messages: SendMessageObject });

  return fetch(REPLY_API_ENDPOINT, {
    method: "POST",
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
      "Authorization": `Bearer ${CH_ACCESS_TOKEN}`,
    },
    body: postDataStr,
  });
};

const signatureValidation = (xLineSignature, channelSecret, body) => {
  const originSignature = crypto
    .createHmac("sha256", channelSecret)
    .update(body, "utf8")
    .digest("base64");
  return xLineSignature === originSignature;
};

http.createServer((req, res) => {
  const { headers, method, url } = req;

  if (url !== "/" || method !== "POST") {
    res.statusCode = 404;
    return res.end();
  }

  let body = "";
  req.on("data", chunk => (body += chunk));
  req.on("end", async () => {
    res.statusCode = 200;
    res.end(); // 先に返す

    const sig = headers["x-line-signature"] || headers["X-Line-Signature"];
    if (!signatureValidation(sig, CH_SECRET, body)) return; // 署名認証エラー

    // 署名認証OK
    const events = JSON.parse(body).events || [];
    await Promise.all(events.map(async (event) => {
      if (event.type !== "message" || event.message.type !== "text") return;
      await httpClient(event.replyToken, [{
        type: "text",
        quoteToken: event.message.quoteToken,
        text: event.message.text
      }]);
    }));
  });
}).listen(PORT);

以下のbodyがパースなどをせずの文字列になってないと上手くいきません。

const signatureValidation = (xLineSignature, channelSecret, body) => {
  const originSignature = crypto
    .createHmac("sha256", channelSecret)
    .update(body, "utf8")
    .digest("base64");
  return xLineSignature === originSignature;
};

何らかの変更はダメらしいのでJSON.Stringify()はここでは利用不可です。

署名の検証を行う前に、署名やリクエストボディの文字列に何らかの変更(文字列の置換、デシリアライズ、エスケープ処理など)をしてしまうと、第三者によって改ざんされたリクエストと区別ができなくなり、署名の検証に失敗します。リクエストボディの文字列にバックスラッシュ(\)や改行(\n)といった特殊なエスケープ文字が含まれているか否かは関係ありません。どのようなリクエストであっても署名の検証を行うより前にデータを変更しないでください。

なのでDifyの中で見た際に最初からObjectで送られてきているともはや検証不可となります。

まとめ

現状Difyの素の機能では厳しそうということがわかりました。

Dify側のアップデートに期待する必要があるのかもしれません。

プロキシサーバーとか前処理があればまた違うのかもしれませんが、その辺までやるなら普通に組みますよね。

もしDifyの設定などでこう突破できるよって話を知っている人がいたら教えてください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?