成果報告 · EA 課程網站
今天原本只是想確認「剛裝的資安工具有沒有用」。查完發現那個工具根本沒在運作,於是改用影片教的方法親自檢查網站——然後找到一個任何人不用登入、不用付錢就能看完你 111 支付費影片的漏洞。這份報告說明:為什麼要改、改了什麼、現在安全在哪裡,以及我自己在過程中犯了哪些錯。
起點不是「網站出事了」,而是一個很單純的問題:我裝的那個資安工具,到底有沒有在保護我?
它的原理是「檢查你寫的程式碼裡有沒有出現某幾個危險的字」,總共只有 9 條寫死的關鍵字。就像一個只會比對黑名單姓名的警衛——名字不在單子上的,他一律放行。
它需要一個叫 python3 的程式來啟動,但你這台 Windows 電腦上的 python3 是微軟商店的一個空殼捷徑,點下去什麼都不會發生。
更麻煩的是,這種工具啟動失敗時不會報錯,會直接放行。所以你以為裝了就有保護,實際上什麼都沒發生。這比沒裝更危險——它給了你一種安全感。
把它的 9 條規則拿去掃你的課程網站,一條都沒對到。而且今天真正找到的那個大洞,它永遠抓不到——因為那不是「寫了危險的字」,而是「把不該給的東西給了不該給的人」,沒有任何可疑關鍵字。
最後的處置:刪掉這個工具,但把 python3 獨立修好(以後其他工具會用得到)。
可以帶走的一句話:評估任何防護工具時,要問的不是「它有沒有裝」,而是「它壞掉的時候,我看得出來嗎?」
七項全部已經上線。下面的章節只是把每一項展開講清楚,你可以挑有興趣的看。
| 項目 | 原本的狀況 | 改成什麼 | 狀態 |
|---|---|---|---|
| 付費影片外流 | 未登入就能拿到全部影片編號,配合一個小技巧即可觀看 | 影片編號不再送到瀏覽器 | 已修復 |
| 舊網址還在漏 | 一個沒清乾淨的舊網址仍在提供有漏洞的舊版本 | 網址已關閉 | 已修復 |
| 資料庫多餘權限 | 只剩一道牆擋著,牆倒了就全開 | 11 張資料表的多餘寫入權限已收回 | 已修復 |
| 沒有流量限制 | 有人可以用程式無限狂送結帳請求 | 每分鐘/每天都設上限 | 已上線 |
| 沒有安全標頭 | 網站可被別人包進假網頁做釣魚 | 加上 6 條防護規則 | 已上線 |
| 沒有內容白名單 | 網頁可以從任何地方載入東西、表單可以送去任何網址 | 完整白名單,且已開啟強制攔截 | 已上線 |
| 套件安全漏洞 | 4 個高風險漏洞 | 升級後歸零 | 已修復 |
這是今天唯一一個「已經在發生中」的問題。以下每一步我都實際跑過,不是推測。
你的課程播放頁有一個側邊欄目錄,列出整堂課的所有單元。為了顯示這份目錄,網站把「整堂課所有單元的資料」送到瀏覽器——包含每一支影片的編號,連沒買的單元也一起送了。
而免費試看單元不需要登入就能打開。兩件事湊在一起,任何人打開試看頁,就拿到了整堂課的影片編號。
用生活比喻:這就像餐廳給客人的菜單上,把保險箱密碼印在背面。菜單本身是要給大家看的沒錯,但不該連密碼一起印上去。
你的影片在 Vimeo 上設了「只有我的網站能播」的限制。聽起來很安全,但這個機制的判斷方式是——看訪客自己填的一張「我從哪裡來」的紙條。
問題是那張紙條是訪客自己寫的,想寫什麼就寫什麼。我實測了三種情況:
| 測試方式 | 結果 | 意義 |
|---|---|---|
| 不填紙條,直接開影片 | 擋下 | 看起來有保護 |
| 紙條填「我從你的網站來」 | 放行,拿到影片 | 保護形同虛設 |
| 紙條填其他隨便的網址 | 擋下 | 確認機制只看那張紙條 |
我進一步確認真的能拿到影片內容,取得了片名「AI課程2-1_ini設定檔介紹」、片長 642 秒、以及實際的影片串流資料。這不是理論上可行,是實際做到了。
這不是你設定錯。這是 Vimeo 這個功能本身的已知弱點,資安研究單位早就公開發表過,官方文件也寫明它是靠那張「來源紙條」判斷的。
原本的計畫是「只對沒買課的人隱藏影片編號」。但我在讀程式碼時發現:那個目錄其實只用影片編號來判斷「這一課是影片還是文字」,根本不需要真正的編號。
所以改成只傳一個「有沒有影片」的是/否標記。這樣一來連已經買課的學員也拿不到編號清單,順便堵掉「學員把清單整串貼到網路上」這條路。
為什麼這件事值得多做?把一串編號貼上網是 3 秒的事,錄下 100 支影片要好幾天。兩者的難度差距非常大。同樣的修改幅度,多堵掉一條路。
這次體檢照著影片的五個問題逐項對答案。除了影片那一項,其他都通過,而且有幾項做得比多數商業網站還紮實。
我直接用「未登入訪客」的身分去資料庫撈資料,實測結果:
折扣碼 → 0 筆 訂單 → 0 筆 會員個資 → 0 筆 課程開通紀錄 → 0 筆 操作紀錄 → 連碰都碰不到
全站 22 張資料表的隔離保護全部開啟,沒有一張漏網。
影片特別強調的「同一毫秒送十個請求」問題,你的網站用的是資料庫層級的搶單機制——同時進來的請求,只有一個搶得到,另一個自動退出。
不會重複開通、期限不會被疊加兩次、折扣碼不會被多算一次。
11 個後台頁面 + 7 個後台功能,全部都會先檢查身分,沒有一個漏掉。被擋下來還會留下紀錄。
「指派管理員」這種高權限操作,另外要求必須是擁有者本人。
結帳時,價格和折扣完全由伺服器自己算,不採信瀏覽器送來的任何金額。條款同意也是後端強制驗證,不是只把按鈕變灰色。
另外全站沒有任何常見的網頁注入風險寫法,金鑰也沒有外洩到前端。
| 影片提出的問題 | 白話說明 | 你的網站 |
|---|---|---|
| ① 盤點資產 | 哪些東西絕對不能外流 | 通過 |
| ② 權限控制 | 誰可以碰哪些資料 | 通過 |
| ③ 信任邊界 | 不要相信瀏覽器送來的東西 | 影片那條破了,已修 |
| ④ 安全鐵律 | 例如「同一筆訂單只能開通一次」 | 通過 |
| ⑤ 攻擊面 | 對外的門有幾道、有沒有限流 | 原本沒有,今天補上 |
這些不像影片那項那麼急,但都是實際存在的缺口。
修好影片漏洞、部署上線之後,我在收尾檢查時發現:你的文件寫著「舊網址已於 6/15 全部刪除」,但實際上還有三個活著,其中一個還停在兩個月前的舊版本——實測仍然在洩漏 19 支影片編號。
也就是說,如果沒發現這個,今天所有的修補都可以被這個舊網址整個繞過。已關閉(現在打開會顯示 404)。
教訓:修完程式碼之後,一定要回頭確認「還有哪些入口指向舊版本」。這一步比修程式本身還關鍵。
你的資料庫有 11 張表,對「一般訪客」和「已登入會員」發了可以寫入資料的權限。目前寫不進去,因為還有另一道規則擋著——但擋住它的只剩這一道牆。
你在 6 月就被這個組合咬過一次(會員可以自己把自己升級成管理員)。當時修了那一條,但其他表的多餘鑰匙沒一起收。今天全部收回了。
原本主站完全沒有任何流量限制——有人寫個小程式每秒送 100 次結帳請求,你的資料庫和金流帳單就會爆掉。
| 保護的功能 | 每分鐘上限 | 每天上限 | 為什麼設這個數字 |
|---|---|---|---|
| 結帳建單 | 5 次 | 30 次 | 真人一分鐘按不到 5 次結帳 |
| 折扣碼試算 | 10 次 | 100 次 | 試算會反覆按,放寬一些 |
| 課程資源下載 | 10 次 | 200 次 | 一堂課的檔案數量有限 |
實測結果(連續呼叫 7 次):
第 1 次 → 放行 今日剩餘 29 第 2 次 → 放行 今日剩餘 28 第 3 次 → 放行 今日剩餘 27 第 4 次 → 放行 今日剩餘 26 第 5 次 → 放行 今日剩餘 25 第 6 次 → 擋下(60 秒後可重試) 第 7 次 → 擋下(60 秒後可重試)
刻意不限制的地方:綠界的付款通知。那是綠界主動打進來告訴你「客人付款了」的,如果限流,真實付款會被擋掉。它本身已有簽章驗證保護。
這是一張「只准從這些地方載入東西、表單只准送去這些網址」的清單,附在網站每一次回應裡交給瀏覽器。就像大樓門口的訪客名單。
它的價值在於:就算哪天有壞東西進到你的頁面,它也送不出去——沒辦法把偷到的資料傳去別的伺服器,也沒辦法把你的付款表單改成送去假的收款頁。
這一項有一個會直接弄壞金流的地雷。你的結帳是「自動送出表單到綠界的網域」。名單如果漏掉綠界,所有付款會在送出的那一刻被瀏覽器擋掉,而且客人只會看到頁面沒反應,你完全不會收到錯誤通知。
所以我的做法是分兩階段,而不是一次開到底:
我做了雙向對照測試,這是關鍵:
只做第一項是不夠的——「沒出現違規」也可能是因為這條規則根本沒生效。加上第二項才能分辨「它放行了綠界」和「它根本沒在看」。
包括:強制使用加密連線、禁止別人用外框把你的網站包起來做釣魚頁、禁止瀏覽器亂猜檔案類型、關閉網站用不到的相機麥克風權限等。
網站框架從 16.2.9 升到 16.3.3。這是小版本更新,不是大改版,但仍然實際重測了 10 個頁面加上你的真實刷卡。
今天傍晚你實際刷了一筆 NT$50。這是唯一能證明「金流沒被改壞」的方法,沒有替代方案。以下是從資料庫撈出來的完整紀錄。
綠界會用兩條路通知你「客人付款了」:一條是把瀏覽器導回你的網站,另一條是伺服器直接通知。這兩條路幾乎同時到達。
今天的紀錄顯示:伺服器通知在瀏覽器那條路已經完成開通後 2 秒才進來,而系統正確認出這是重複的,直接略過。
如果沒有這個機制,這一筆會被開通兩次、課程期限被多加一次、折扣碼被多算一次。這不是我測出來的,是正式環境自己跑出來的。
同時也驗證了流量限制真的接上正式流量——你操作的每一步都被記錄下來:
折扣碼試算 18:29:58 結帳 18:30:10 折扣碼試算 18:31:23 結帳 18:31:28
全程零警告、零嚴重錯誤。
報喜不報憂的報告沒有價值。這些錯全部都被抓出來並修正了,但你應該知道它們發生過——尤其是有幾個如果沒發現,會留下真正的問題。
| 我做錯什麼 | 怎麼發現的 | 影響 |
|---|---|---|
| 把錯的修法寫進了長期筆記 修 python3 的方法我寫了「放在某資料夾、一個檔案就好」——實際動手才發現資料夾沒有寫入權限,而且需要兩個檔案不是一個 |
真的動手做的時候連撞兩次牆 | 已回頭改正 否則下次會照著錯的做 |
| 說你的網站「沒有 middleware」 實際上有,只是新版框架把它改了名字叫 proxy |
建立新檔案時系統報錯「已經有一個了」 | 影響輕微 但這個判斷曾寫進體檢結論 |
| 誤判正式網站「畫面壞掉了」 看到截圖排版怪異就先喊停 |
去量實際數值,發現是瀏覽器視窗被縮窄,顯示的是正常的手機版排版 | 虛驚一場 |
| 差點把開發模式的測試結果當成正式結果 開發模式量到「需要放寬某項限制」,如果照抄就會為了本機工具永久降低正式站的安全性 |
改用正式版本重測,發現那是開發模式特有的假訊號 | 已避免 這個如果沒發現會留下真的缺口 |
| 把自己的測試殘留當成程式 bug 測流量限制時 7 次全被擋,一度以為寫錯了 |
去查資料表,發現是前一次測試已經用掉 5 次額度,屬於正確行為 | 虛驚一場 |
| 誤報一個不存在的問題 說組合結帳頁不會顯示錯誤訊息 |
再查一次,訊息其實有顯示,只是寫在另一個檔案裡 | 已更正 |
| 多建了一個重複的設定檔 沒先查就新建,其實早就有一份 |
啟動時系統讀的是舊的那份 | 已清除 |
| 一度把既有問題當成自己造成的 正式站出現一個技術警告,我先前判斷「只是測試殘留」 |
拿兩個月前的舊版本做對照,證實舊版也有一模一樣的警告 | 既有問題 不是今天造成的 |
從這些錯誤裡整理出的三條原則:
每一項改動都可以還原。這些指令不用你自己記——把這一段給任何 AI 看,它就知道怎麼做。
| 要退回什麼 | 怎麼做 | 會不會有損失 |
|---|---|---|
| 整個網站退回今天之前 | Vercel 後台一鍵回滾到舊版本 | 不會,舊版本都還留著 |
| 內容白名單(怕擋錯東西) | 把 CSP_ENFORCE 改回 false 重新部署 | 不會,退回「只記錄不擋」 |
| 流量限制 | 移除資料庫裡新增的那張表與函式 | 不會,都是今天新增的 |
| 資料庫權限 | 重新發放權限即可 | 不會,一行指令 |
python3 修正 | 刪掉新增的那兩個檔案 | 不會 |
# 退回內容白名單(改完要重新部署) # 檔案:proxy.ts const CSP_ENFORCE = false; # 重新部署 cd C:\Users\COSH\dev\course-site npx vercel --prod --yes
一個要記得的新規則:內容白名單現在會真的攔東西。以後要加任何新的外部服務(新的分析工具、新的字型、換金流商),都必須先加進白名單,否則會被靜默擋掉——不會報錯,只是那個功能就是不會動。
都不緊急,但你應該知道它們存在。
今天堵掉的是「未登入就能拿到編號」這條路。但買了課的學員仍然拿得到自己那堂課的編號,理論上可以外流。
要根本解決,需要影片本身能認人(例如每次播放發一張會過期的通行證)。但我查證過:你的 Vimeo 方案做不到——這個功能只在企業方案,加購價約每年 2,000 美元。
現階段的決定:不處理。理由是你目前的學員數是個位數,花這筆錢不划算。三個回頭評估的時機:學員數破 100 / 發現實際外流跡象 / 要開更高價的課。
播放頁在正式站會出現一個技術警告。我用兩個月前的舊版本對照過,舊版也有一模一樣的警告,不是今天造成的。它不影響任何功能(系統會自己修正重畫),值得之後單獨排查。
AQ178817221056244(你第一次建單但沒完成的那筆)test2430 折價券還是啟用狀態——建議停用,免得外流比照 6/16 清測試資料的做法即可。
純整理事項,跟資安無關。但今天的教訓提醒我們:舊部署配上沒清乾淨的網址,就會變成繞過所有修補的後門。建議之後排一次清理。