退訂連結不該把 email 放進網址
客戶專案踩坑
Ricky-
08 月 22,2026

每封信底部都要有「退訂」。最直覺的寫法是 /unsubscribe?email=someone@x.com&token=…——網址帶著 email 和一個簽章。但這個設計有兩個藏在細節裡的問題。

不安全 /unsubscribe?email=someone@x.com&token=hmac(email) ✗ email 留在日誌 / 歷史 / referrer ✗ 固定簽章永不失效 → 誰拿到都能退掉他 安全版 /unsubscribe?t=9f3c…隨機、與 email 無關 GET 只顯示「確認退訂」頁 · 按下的 POST 才真的退訂
把 email 從網址拿掉、改用不可猜的隨機 token,再用 GET/POST 兩段式。

兩個問題

一、email 攤在網址上。網址會被記進伺服器日誌、瀏覽器歷史、referrer,甚至被轉寄——等於把訂閱者的信箱到處留痕。

二、退得掉別人。如果 token 是用 email 算出來的固定簽章,它永不失效;任何拿到那條連結的人(轉寄、側錄、日誌)都能隨時退訂那個人

安全版設計

不可猜的隨機 token。每個訂閱者發一組跟 email 無關的隨機 token,存在資料庫。退訂網址只帶 token、完全不帶 email——退不了別人,也不外洩信箱。

兩段式:GET 只確認、POST 才退訂。很多信箱與防毒會自動「點開」信裡的連結去掃描;如果 GET 就退訂,會被這些掃描器誤觸。所以 GET 只回一個「確定要退訂嗎」的頁面,按下按鈕送出的 POST 才真的改狀態。

// 退訂只認隨機 token;GET 安全、POST 才有副作用
GET  /unsubscribe?t=<random>   → 顯示「確認退訂」頁(不改狀態)
POST /unsubscribe  { t }       → 查到訂閱者 → 設為已退訂 + 通知站長

// token 產生:與 email 沒有任何可推導關係
unsubToken = crypto.randomBytes(32).toString("hex"); // 唯一、索引
…/unsubscribe?t=9f3c… (GET · 不改狀態) 確定要退訂新文章通知嗎? 確認退訂 ↳ 按下 = POST,這一步才真的退訂
GET 只給一個確認頁;掃描器就算自動點進來,也不會動到任何人的訂閱狀態。

眉角

  • 「GET 要安全、冪等」不是教條。這裡就是它保護你不被信箱掃描器誤退訂的實例。
  • 不透露、不重複。無效 token 一律回同一個「連結無效」頁,不告訴對方 token 是否存在;已退訂者再點也不重複通知站長。
  • 順手加值:退訂成功也通知站長「是誰退的」,掌握名單變化。
帶得走的一句話

退訂連結是少數「陌生人手上就有」的入口。把 email 從網址拿掉、用不可猜的隨機 token、再用「GET 確認 / POST 執行」擋掉自動掃描——退訂就從「誰都能退掉誰」變成「只有本人點得動」

Tags:
資安電子報


相關文章
一個沒帶 req 的 update,讓「發新文章」噴 404
一個沒帶 req 的 update,讓「發新文章」噴 404
ricky - 2026-08-22T09:12:07.878439Z
afterChange hook 的 update 沒帶 req,讓「新建即發佈」在交易未 commit 時查不到自己而 404,以及一行修法。
Neon 睡著時,別讓使用者看到 500
Neon 睡著時,別讓使用者看到 500
ricky - 2026-08-22T09:10:16.630257Z
幫旅遊部落格客戶處理 Neon serverless Postgres 冷啟動 500 的退避重試:只重試暫時性連線錯誤、指數退避、驗證錯誤不重試。
怎麼寄電子報不被 Gmail 鎖
怎麼寄電子報不被 Gmail 鎖
ricky - 2026-08-22T09:12:11.341353Z
新文章通知所有訂閱者:背景送、一人一封、逐封節流,避免擋請求、外洩收件人與被 Gmail 判濫發。
訂閱電子報
隨時掌握最新的創業故事與設計開發過程
內容包含來自麒航團隊的日常分享、專業知識與工具推薦,助你了解更多創業背後的思維與技術。
麒航私房推薦
Clelereve Blog LogoClelereve Blog LogoClelereve Blog Logo
廣告區
Clelereve Blog Logo
聯絡我們
任何合作或問題請洽:
clelereve@gmail.com
Follow us
Copyright © 2025 blog.clelereve.com - All Rights Reserved
Frontend Version: -- | Backend Version: --
創業實驗筆記 - Clelereve Blog | 退訂連結不該把 email 放進網址 | Clelereve Blog