(04) 2375-8388 台中在地服務
企業 IT 整合 · 資安 · 維運 原廠技術合作夥伴
網站資安 / 事件後 postmortem

企業 SSL 憑證自動續期失靈 8 天才發現:三層防護策略實戰

Let's Encrypt 自動續期成功但 IIS 拿到的 cert 無法 TLS handshake,網站壞 8 天沒人發現。三層防護:UptimeRobot 5 分鐘外部監測、hourly self-healing、win-acme Script post-renew hook 0 秒延遲修復。

網站資安 · 2026 年 5 月 · 凱茂資訊技術團隊 · 閱讀時間 10 分鐘
分享: LINE 分享
快速回答:Let's Encrypt 自動續期可能「成功但實際失敗」——win-acme 把新 cert 裝進 IIS 但 cert ↔ private key 的 schannel metadata 沒設好,TLS handshake 立刻 reset。傳統依賴 Scheduled Task LastResult 沒人會定期看;Sentry 因為 HTTPS 壞了根本載不到;開發人員 hosts override 自己訪問本來就 https 失敗——三道警戒線同時失效,網站壞 8 天直到使用者體感到。修法是建三層防護:UptimeRobot 5 分鐘外部 HTTPS check + Hourly self-healing Scheduled Task + win-acme renewal.json 加 Script install plugin 0 秒延遲修 metadata。

事件時間線:4/19 遷移 → 4/27 renew → 5/5 才發現

  • 4/19:web server 從 nginx 切換到 IIS。原 nginx-era 的 scripts/post-renew.ps1 沒同步重寫,仍含 Restart-Service nginx 邏輯。Scheduled Task SSL-PostRenew 每天 06:00 跑,每天 exit 1 失敗,但沒人看 LastResult。
  • 4/27 08:32:Let's Encrypt 自動 renew,新 cert 進 LocalMachine\My,win-acme 用 IIS install plugin 把 cert 綁定到 IIS site binding。但 cert ↔ private key 的 schannel metadata(KeyProvInfo)沒設好——cert 安裝成功,private key 在 LocalMachine\Crypto\RSA 內,但 schannel TLS handshake 找不到對應 key file。
  • 4/27 之後 8 天:所有 HTTPS connection TLS handshake reset。Cloudflare cache 部分頁面繼續 serve(看起來像「偶爾壞」),origin 直連完全壞。
  • 5/5 18:42:使用者體感到網站進不去回報。19:30 修好——certutil -repairstore My <thumbprint> 重建 cert ↔ key metadata 關聯,schannel 立即生效,IIS 不需重啟。
為什麼 8 天才被發現?三道警戒線同時失效
為什麼 8 天才被發現?三道警戒線同時失效

為什麼 8 天才被發現?三道警戒線同時失效

事件後做 postmortem,列每一道應該攔下的機制:

  • 沒有外部 uptime monitoring。沒有任何 service 從外網每幾分鐘 ping 首頁。
  • Sentry 監測 SDK 載不到。HTTPS handshake 階段就 reset,瀏覽器根本沒下載 JS 檔案,error tracking SDK 從未啟動。「網站壞了 Sentry 會通知」這條假設依賴 SDK 至少能載入。
  • 開發人員 hosts override 直接訪問 origin。dev 機器把 www.kmau.com.tw 指向內網 IP,origin 沒裝對外的 cert,所以 dev 自己訪問本來就 https 失敗。「真壞」跟「平常 hosts 失敗」沒分別。
  • Cloudflare cache 部分 stale 繼續 serve。某些 user 看到舊頁面,不一定意識到網站已壞。Cache hit 反而蓋住了完全失效的事實。
  • Scheduled Task LastResult 純背景。SSL-PostRenew 每天 exit 1,但沒 alert pipeline,沒人定期看 schtasks。

每一道都是「正常情況下會發現」的機制,但都依賴前一道工作。一旦多道同時失效,故障靜默降級的時間 = MTTR。

Root cause:cert install ≠ schannel ready

真正 root cause 不是 nginx 殘留 script——那是觸發點,不是真原因。真原因是:

win-acme 的 IIS install plugin 把 cert 安裝到 LocalMachine\My,更新 IIS site binding,但沒可靠地設定 schannel 需要的 KeyProvInfo metadata(PROV_INFO 結構)

cert 跟 private key 在憑證儲存區看起來都在,但 schannel 找不到 cert 對應的 key file。對 IIS 來說 binding 看起來正常,對 schannel TLS 處理來說整個 handshake 用不出 key 來簽——直接 RST 連線。

certutil -repairstore 工具重建這個 metadata,把 cert thumbprint 跟 RSA key file 在 KeyProvInfo 上關聯起來。修完後 schannel 立即可用,不需要重啟 IIS

三層防護方案(彼此獨立、互為 backup)
三層防護方案(彼此獨立、互為 backup)

三層防護方案(彼此獨立、互為 backup)

Layer 1:外部 uptime monitoring(5 分鐘 alert)

用 UptimeRobot / Pingdom / Better Uptime 等外部服務每 5 分鐘對首頁做 HTTPS HEAD。任何 5xx / TLS handshake reset / connection refused 立即 email + 推播。

關鍵特性:服務在外網、不依賴自家網站任何元件——即使整個 origin 都炸了也照常 alert。免費版即可,不需要付費版的「cert expiry 提前 30 天通知」(HTTPS check 在 cert 到期當天 5 分鐘內也會 alert,本來就 cover)。

從 8 天 → 5 分鐘,這一道就把事件影響範圍縮 99.96%。

Layer 2:主機端 hourly self-healing

Scheduled Task 每小時跑 idempotent 腳本:

  1. LocalMachine\My 內最新(NotAfter 最遠)的 cert
  2. probe RSA private key accessibility(嘗試讀 KeyProvInfo)
  3. 不通就 certutil -repairstore My <thumbprint>
  4. netsh http show sslcert 驗證 IIS binding(避免 hairpin NAT 直接做 outbound HTTPS)
  5. 失敗就 POST 到 /api/error-report → Sentry alert

worst case 從 24 小時降到 1 小時。注意是 idempotent——成功狀態下重跑 100 次也不副作用。

Layer 3:ACME post-renew Script hook(root cause fix)

真正解決問題的不是 mitigation,是從根源消除 metadata 不一致。win-acme 的 renewal config 加 Script install plugin(與 IIS install plugin 並列):

{
  "InstallationPluginOptions": [
    { "Plugin": "iis", ... },
    { "Plugin": "script",
      "Script": "C:\\inetpub\\kmauWeb\\scripts\\post-renew.ps1",
      "ScriptParameters": "-Thumbprint {CertThumbprint}" }
  ]
}

renew 後 win-acme 會依序:(1)IIS plugin 安裝 cert + 更新 binding、(2)Script plugin 立刻跑 post-renew.ps1 修 metadata。0 秒延遲,不靠 hourly safety net。

這層才是 root fix——下次 cert renew 完成那一刻 metadata 就對了。Layer 1 跟 Layer 2 變成「萬一 Layer 3 也壞了」的多重保險。

Mitigation vs Root fix:差別在哪?

事件當天為了讓網站立刻好,跑 certutil -repairstore 是 mitigation——治了當下,沒解未來。下次 renew 還會壞。

真 root fix 是讓 renew 的那一刻就修好,不靠 hourly poll。差別:

方案修復延遲覆蓋未來類型
事件當下 certutil -repairstore0(只當下)Mitigation
Layer 2 hourly self-healing≤ 1hSafety net
Layer 3 win-acme Script hook0Root fix
Layer 1 UptimeRobot5 min(alert,不修)Detection

三層各司其職:Detection(你發現) + Self-healing(系統自修) + Root fix(從源頭不再壞)。任何一層失效,其他兩層仍 cover。

架構遷移後的審查 SOP
架構遷移後的審查 SOP

架構遷移後的審查 SOP

事件追到底,根因是「nginx → IIS 遷移時沒清舊架構的 Scheduled Task / script」。post-renew.ps1 從遷移那天起 16 天每天都失敗,但失敗對 happy-path(cert 沒 renew)沒影響。直到 4/27 renew 觸發新路徑才壞。

未來架構切換的審查 SOP:

  1. 列出舊架構所有 Scheduled Task / cron / hook script(一個都不能漏)
  2. 逐個評估:刪除 / 重寫 / 保留(含解釋為何保留)
  3. 砍 service 同時砍 file(如 C:\nginx\ 直接 rename 成 .deprecated.YYYYMMDD
  4. 切換後 prod 至少跑一次真實的 hot path(如手動 renew 一張 cert),不只看 happy path
  5. Postmortem 寫進 runbook,下次同類遷移直接照單檢查

給其他企業 IT 主管的 5 個 takeaway

重點摘要

  • 外部 uptime monitoring 不選——任何企業網站都該有。免費版每 5 分鐘 HTTPS check 就把 MTTR 從天級降到分級,沒理由不裝。
  • Sentry / 自家 monitoring 不能當唯一警戒線——它依賴你的網站還能載 JS。HTTPS handshake 階段壞了,Sentry 不會通知你。
  • 「自動化」不等於「不會壞」——LE 自動續期是過程自動化,不保證結果正確。每個自動化都要有 fail-safe + alert。
  • 架構遷移必審 legacy script——切換 web server / DB / framework 時,舊架構的 cron / Scheduled Task 通常被忘記。「砍 service 順手砍 script」是流程紀律。
  • Mitigation 是急救,不是治療——事件後一定要追到 root cause,否則下次同樣方式會再壞。

需要協助設計企業網站的 SSL / TLS 監測、ACME post-renew hook、或事件後 postmortem 流程?凱茂提供包含 Layer 1-3 完整方案的網站維運諮詢。

預約 30 分鐘免費網站維運架構諮詢 →

相關方案:資安與網路方案

凱茂資訊為您提供完整的規劃、建置與維運服務,歡迎諮詢。

瞭解我們的資安防護方案 → 索取報價

常見問題

Let's Encrypt 自動續期成功,為什麼網站還會壞?

ACME 客戶端(如 win-acme、Certbot)會把新 cert 安裝到憑證儲存區並更新 IIS binding,但 Windows schannel 需要 cert 跟 RSA private key 在 metadata 上正確關聯(KeyProvInfo)。某些情境下 install 完成但 metadata 沒設好,IIS 拿到新 cert 卻無法用 private key 簽 TLS handshake——使用者連線立刻被 reset,但 ACME 客戶端 log 顯示 success。這是 LE 自動續期最隱蔽的失敗模式。

為什麼 8 天都沒人發現?

三個原因同時失效:(1)沒外部 uptime monitoring,沒人從外網每幾分鐘 ping。(2)Sentry 監測 SDK 載不到(HTTPS handshake reset 階段就斷),所以連 Sentry 都沒收到 event。(3)開發人員 hosts override 直接 IP 訪問 origin,本來就 https 失敗,所以「真壞」沒分別。每一道都是「正常情況下會發現」的機制,但都依賴前一道工作。8 天才被使用者直接體感到。

為什麼 hourly self-healing 不夠?要 0 秒延遲?

Hourly self-healing 是 mitigation 不是 root cause fix——它把壞掉時間從 24 小時降到 1 小時,但仍然有壞掉時間。真正 root cause fix 是 ACME 客戶端的 install plugin chain:cert install 完立即跑 post-renew script 修 metadata。win-acme 設定加 Script install plugin(IIS plugin 之外),renew 後 0 秒延遲修好。三層防護 = root fix + hourly safety net + external monitoring。

certutil -repairstore 修了什麼?

certutil -repairstore My <thumbprint> 重建 cert ↔ RSA private key 的 KeyProvInfo metadata(PROV_INFO 結構),把 cert 跟 LocalMachine\Crypto\RSA 內的 private key file 關聯起來。對 schannel 來說相當於告訴它「這個 cert 用這個 key 簽」。修復後 IIS 不需重啟、TLS handshake 立即可用。注意需要 admin 權限。

外部監測一定要付費版的 cert expiry 通知嗎?

不需要。免費版 5 分鐘 HTTPS HEAD 檢查在 cert 到期當天 / TLS handshake reset 5 分鐘內就會 alert——cert 到期後 HTTPS check 必失敗。付費版的 cert expiry 提前通知(如 30 / 14 / 7 天前)對「忘記 renew」場景有用,但對「自動續期成功但 install 失敗」這種主流故障模式,免費版 HTTPS check 已足夠。先用免費版開始,有預算再升級。

IT 技術電子報

訂閱 IT 技術電子報

每月精選 IT 趨勢與實務文章,直接送到你的信箱

專業顧問諮詢

讀完這篇文章,是否有更多問題?

凱茂資訊提供 30 分鐘免費架構評估,由專業顧問針對您的企業現況給出具體建議,不推銷、不強迫。

預約 30 分鐘免費諮詢 免費 IT 健檢 · 5 分鐘

✓ 免費諮詢,無義務購買 ✓ 中部地區可現場拜訪 ✓ 一般於 1 個工作天內回覆

想了解凱茂能幫上什麼? · 5 分鐘免費健檢,1 個工作天內回覆