LINEのBotをGoogle Apps Script(GAS)で作ると、公式ドキュメントに書かれている「署名の検証」が、どう頑張っても実装できません。
コードの書き方が悪いのではありません。GASの doPost は、届いたリクエストのヘッダーを一切受け取れない からです。LINEの署名はヘッダーに入って届くので、GASにいる限り、その署名を読む手段がありません。
私(UMEO CREATE・伊藤)がこれに正面からぶつかったのは、2026年8月12日です。自社で毎日使っているLINEのAI秘書(予定の確認やメールの下書きをLINEから頼める仕組み)を、配布用に整理していたときでした。設定ファイルを1行ずつ見直していて、背筋が冷える記述を見つけました。
この記事では、そのとき何が起きていたか、GASではなぜ署名検証ができないのか、そして代わりにどう守ったかを、実際のコードの考え方と一緒に書きます。GASでLINE Botや外部から呼ばれる仕組みを作っている方は、一度ご自分の環境と見比べてみてください。
夜の事務所で、ノートパソコンに向かい険しい表情で画面を確かめる男性
この記事の目次(8項目) 何が起きていたのか:URLを知っていれば誰でも「私」になれた GASのdoPostが受け取れるもの、受け取れないもの 代わりにやったこと:GASで確実に読めるものだけで2段の守りを作る 正直に書く、合言葉方式の弱点 どうしても署名検証が必要なら:前に1段、中継を置く 実際の反映で踏んだこと GASで外部から呼ばれる仕組みを作るときのチェックリスト まとめ:「公式の正攻法が使えない」ときこそ、何で守るかを決めておく
何が起きていたのか:URLを知っていれば誰でも「私」になれた
見つけたのは、デスクトップアプリの設定ファイル(初期値のひな形)に、GASの本番URL(/exec で終わるWebアプリのURL)がそのまま書かれていた ことです。
これだけなら「URLが見えているな」で済みそうですが、実際には次の3つが重なっていました。
1. GASのWebアプリは、LINEから呼んでもらうために 「アクセスできるユーザー:全員(匿名)」 で公開していた 2. そのWebアプリは 私のGoogleアカウントの権限で動く 設定だった(カレンダー・Gmail・Tasksを操作するため) 3. リクエストを受けたときに 「誰からの依頼か」を確かめる処理が何もなかった
つまり、URLさえ手に入れれば、第三者が私のGoogleアカウントとしてカレンダーを書き換え、Gmailを読み、メール送信の命令まで通せる状態 でした。配布用のファイルをそのまま誰かに渡していたら、URLも一緒に渡っていたことになります。
実害が出る前に気づけたのは、配布の前に「中身を1行ずつ読む」工程を挟んでいたからです。逆に言えば、自分だけで使っているうちは、この穴にまったく気づきませんでした。 動いているものは疑わないからです。
GASのdoPostが受け取れるもの、受け取れないもの
LINEの公式ドキュメントでは、Webhook(LINEから自分のサーバーへの通知)を受けたら、まず署名を検証するよう求めています。仕組みはこうです。
LINEは、送る本文をチャネルシークレット(LINEと自分だけが知っている鍵)で計算した値を、x-line-signature というヘッダー に入れて送ってくる 受け取った側は、同じ本文と同じ鍵で計算し直し、ヘッダーの値と一致すれば「本物のLINEから、改ざんされずに届いた」と判断できる
ここで問題になるのが、GASの doPost(e) に渡される e の中身です。Googleの公式ドキュメントに載っている項目は次のとおりです。
項目 中身 e.parameter / e.parametersURLのクエリ文字列(?a=1&b=2 の部分)や、フォーム送信の値 e.queryStringクエリ文字列そのもの e.postData送られてきた本文(contents)、その種類(type)、長さ e.contentLength本文の長さ e.pathInfoURLの /exec より後ろに付いた部分
ヘッダーの項目がありません。 x-line-signature も、Authorization も、GASのコードからは見えないのです。
少しややこしいのは、GASには署名の計算に使う関数(Utilities.computeHmacSha256Signature)自体はあることです。計算はできるのに、比べる相手(LINEが送ってきた署名)が手元に届かない。 検索すると署名検証のサンプルコードが見つかることがありますが、GASの doPost ではヘッダーを取り出す行が動きません。
ですから、「LINE Botなら署名検証をすれば安全ですよ」という一般的な助言は、GASで作っている限り、そのままでは使えません。私も最初は、LINE_CHANNEL_SECRET(チャネルシークレット)を設定に持っていながら、使い道がない状態でした。
ホワイトボードに描いた矢印の図を指しながら、封筒を手に同僚へ説明する女性
代わりにやったこと:GASで確実に読めるものだけで2段の守りを作る
ヘッダーが読めないなら、GASで確実に読めるもの で守るしかありません。私が実装したのは、次の2段です。
1段目:URLに「合言葉」を付ける
GASで唯一確実に読めるのは、URLのクエリ文字列です。そこで、推測できない長い文字列を「合言葉」として作り、URLの末尾に付けたものだけを受け付ける ようにしました。
https://script.google.com/macros/s/(ID)/exec?key=(推測できない長い文字列)
doPost の一番最初で、この合言葉が正しいかを確かめます。合言葉の本体はコードに書かず、GASの「スクリプトプロパティ」(プロジェクトごとの設定保管場所)に保存します。
function doPost(e) {
var expected = PropertiesService.getScriptProperties().getProperty('WEBHOOK_TOKEN');
var given = (e && e.parameter && e.parameter.key) || '';
if (!expected || given !== expected) {
return ContentService.createTextOutput('ng');
}
// ここから先が本来の処理
}
この方式の良いところは、LINE側の設定を「Webhook URLの貼り替え」だけで済ませられる ことです。デスクトップアプリ側も、呼び出し先のURLを差し替えるだけで、コードの変更は要りませんでした。
2段目:LINEの「誰から来たか」で、持ち主以外を無視する
合言葉だけでは、まだ穴があります。LINEのBotは、友だち追加さえすれば誰でも話しかけられる からです。正しいURLを通って、正しくLINEから届いた、見知らぬ人のメッセージも処理してしまいます。
そこで、LINEから届く本文に含まれる送り主のユーザーIDを見て、設定しておいた持ち主のIDと一致しないものは、黙って無視する ようにしました。自分専用の秘書なので、話しかけてよいのは自分だけ、という割り切りです。
この2段で、「URLを知っているだけの第三者」と「友だち追加しただけの第三者」の両方を締め出せます。
カフェのテーブルで、スマートフォンと手帳を見比べながら予定を確かめる男性の手元
正直に書く、合言葉方式の弱点
この方式は、LINE公式の署名検証と同じ強さではありません 。導入するなら、弱点も知ったうえで使ってください。
① 本文の改ざんは見抜けない 署名検証は「本文が途中で書き換えられていないか」まで確かめられますが、合言葉は「正しい鍵を持っているか」しか見ていません。通信自体はHTTPSで暗号化されているので現実的な危険は小さいものの、仕組みとしては一段弱いと理解しておくべきです。
② URLが漏れたら、それで終わり 合言葉はURLの一部なので、URLを貼った場所(LINE Developersの設定画面、アプリの設定ファイル、チャットの履歴)すべてが「鍵の保管場所」になります。URLを人に見せる、画面共有で映す、設定ファイルごと渡す 。どれも鍵を渡すのと同じです。今回の発端がまさにこれでした。
対策として、合言葉は作り直せる ようにしてあります。作り直した瞬間に古いURLは使えなくなるので、漏れた疑いがあれば、新しいURLに貼り替えるだけで元の安全な状態に戻せます。
③ 合言葉の作り方 合言葉は、推測できない十分に長い文字列にします。私の最初の実装では Math.random() で32文字を作っていました。すぐに破られる性質のものではありませんが、暗号用途の乱数ではないので、これから作るなら Utilities.getUuid() を2つつなげる ほうが素直です。
④ 「設定し忘れ」をどう扱うか 私の実装では、稼働中の秘書を止めないために、合言葉が未設定のあいだは従来どおり受け付ける 作りにしました。コードの反映と合言葉の設定の間に、LINEからの通知が全部はじかれる時間を作らないためです。ただしその分、設定を忘れると無防備なまま動き続けます 。そこで、いまの守りの状態(合言葉は設定済みか、持ち主のIDは設定済みか)を1回の実行で表示する確認用の関数も用意しました。新しく作るなら、最初から「未設定なら受け付けない」にしておくほうが安全です。
どうしても署名検証が必要なら:前に1段、中継を置く
扱う情報が重く、合言葉では足りない場合は、ヘッダーを読める実行環境を前に置き、そこで署名を検証してからGASへ渡す 構成にします。Google Cloudの Cloud Run や、Cloudflare Workers のような、小さなプログラムを置ける場所です。
LINE → 中継(ここで x-line-signature を検証)→ 合言葉付きURLでGASへ 検証に失敗したものは、中継の段階で捨てる
ただし、管理する場所が1つ増えます 。中継の保守、鍵の二重管理、障害時にどちらが原因かの切り分け。社内の小さな仕組みにとっては、この手間のほうが重くなることも多いです。私の判断では、自分や社内の数人だけが使うBotなら「合言葉+持ち主の限定」で十分 、お客様の個人情報や決済に関わるなら中継を置く 、というのが線引きです。
実際の反映で踏んだこと
守りのコードそのものは、説明のコメントを含めて160行ほどのファイル1枚と、既存の受付部分への数行の追加で済みました。手間取ったのは反映のほうです。
同じコードのコピーが4つあった デスクトップ版の秘書が2種類、配布用のひな形、LINE版の原本。もとは1つだったものを複製して育てていたので、同じ穴が4か所に開いていました 。1か所直して安心していたら、残りの3つは無防備なままです。複製で増やした仕組みは、直すときに「コピーがいくつあるか」から数える必要があります。
合言葉を有効にした瞬間、古いURLはすべて止まる 合言葉を設定したら、その場でLINEのWebhook URLとアプリの呼び出し先を貼り替えます。順番を逆にしたり、間を空けたりすると、その間のLINEからの通知はすべてはじかれます。「設定する」と「貼り替える」は、ひと続きの作業として一度にやる のがコツです。
コードを直しても、本番のURLには反映されない GASは、コードを保存(clasp push)しただけでは公開中のWebアプリは古いままです。デプロイを更新して初めて、本番のURLに新しいコードが載ります。このあたりは本番を2回壊して学んだ、GASデプロイ後に必ず見る3つ と、GAS改修で本番事故を防ぐ確認4選 に詳しく書きました。
紙のチェックリストに印を付けながら、ノートパソコンを操作する人の手元
GASで外部から呼ばれる仕組みを作るときのチェックリスト
最後に、LINE Botに限らず、フォームやほかのサービスからGASを呼ぶ仕組みを作るときに、私が確認している項目をまとめます。
Webアプリの公開範囲が「全員(匿名)」なら、受け付ける前に相手を確かめる処理 があるか 本番URLが、設定ファイルのひな形・資料・チャット履歴に直書きされていないか 合言葉や鍵はスクリプトプロパティ に置き、コードに書いていないか LINE Botなら、持ち主(または許可した人)のユーザーID以外 を無視しているか メール送信のような取り返しのつかない操作 を、外から来た命令だけで実行できないか 合言葉を作り直す手順 が決まっているか 同じコードのコピーがいくつあるか 把握しているか
まとめ:「公式の正攻法が使えない」ときこそ、何で守るかを決めておく
GASは、サーバーを借りずに業務の仕組みを作れる、中小企業にとってとても心強い道具です。その一方で、ヘッダーが読めない というように、一般的なWebサーバーなら当たり前にできることが、いくつかできません。
大事なのは、それを知らずに「公式どおり作ったつもり」でいないことです。署名検証が使えないなら、合言葉と持ち主の限定で守る。それでも足りない重さなら、中継を置く。どこまで守れていて、どこからが弱いのかを、作った本人が説明できる状態 にしておくのが、社内で使う仕組みの最低ラインだと考えています。
UMEO CREATEでは、GASとGoogle環境を土台にした業務システムを、作るところから保守まで担当しています。今回のような守りの設計も、最初の見積の段階から含めてご提案しています。
▼ 業務システム開発についてhttps://www.umeocreate.com/system.html
【群馬・北関東の中小企業・ひとり社長のAI導入をサポート】
UMEO CREATE(ウメオクリエイト)では、群馬県(高崎・前橋・太田・伊勢崎・吉岡町など)を中心に、中小企業や個人事業主・ひとり社長様向けの「AI導入・業務自動化・DX伴走コンサルティング」を行っています。
「自社の業務でもAIを使える?」「何から手をつければいいかわからない」「面倒な事務作業を自動化したい」といったご相談は、お気軽に公式LINEまたはWebサイトのお問い合わせフォームよりご連絡ください。