No1. 喜傑獅「一讚折一元」事件:改 hosts 為什麼能繞過 Cloudflare?
2024 年喜傑獅(CJSCOPE)「一讚折一元」促銷引發的爭議,在二審判決出爐後又成為討論焦點。新聞常把事件濃縮成「消費者修改 hosts,繞過 Cloudflare 進入網站下單」,有些討論則直接稱為破解或入侵。
從技術角度看,這些說法把多個不同層次混在一起了。修改 hosts 不會破解 Cloudflare、不會改動公開 DNS,也不會取得伺服器權限;它只改變一台電腦將 hostname 解析成哪個 IP。真正的問題是:Cloudflare 後方的 Origin 仍可從 Internet 直接連線,而且應用程式仍接受訂單、寫入資料並寄出通知。
Cloudflare 保護的是「經過 Cloudflare 的流量」。Origin 若仍接受任意來源直接連線,知道 Origin IP 的 client 就可能建立另一條不經 Cloudflare 的連線路徑。
以下分析不判定個案的法律責任,也不推測當事人取得 Origin IP 的方法。重點是釐清 DNS、TCP、TLS SNI、HTTP Host、Reverse Proxy 與訂單狀態之間的關係。
喜傑獅「一讚折一元」事件與證據範圍
二審判決記載,喜傑獅於 2024 年 11 月 24 日發布促銷活動,貼文最後累積約 1.7 萬個讚。業者主張網站出現異常流量及 DDoS 警告,因此在 11 月 25 日 21:06 通知代管公司進行網站關閉操作;消費者則在當日 21:57 左右,以 Linux 修改 hosts 的方式連入網站、送出訂單,並收到系統通知。
判決進一步記載,21:06 的操作是修改 Cloudflare 端的設定以封鎖一般訪問請求,並沒有停止 Origin 主機。該操作之後,系統仍產生 516 筆訂單。這兩項資訊是整起事件最重要的技術背景:臺灣士林地方法院 114 年度消小上字第 3 號判決。新聞對判決結果及主要過程另有摘要,可參考聯合新聞網的事件報導。
事件時間線
| 時間(UTC+8) | 公開資料記載 | 技術意義 |
|---|---|---|
| 2024-11-24 晚間 | 發布「一讚折一元」促銷 | 流量與訂單需求上升 |
| 2024-11-25 21:06 | 代管公司調整 Cloudflare 設定 | 正常入口受到限制,Origin 主機未停止 |
| 2024-11-25 21:32 | 消費者私訊表示官網無法進入 | 當事人知道正常連線路徑異常 |
| 2024-11-25 21:57 | 消費者以 Linux 修改 hosts 後送出訂單 |
請求以不同路徑到達仍在運作的應用程式 |
| 關閉操作後 | 系統仍產生 516 筆訂單 | Edge 層措施沒有同步停止所有訂單副作用 |
| 2024-11-26 01:07 | 相關教學影片發布 | 晚於本案訂單,不是本案當事人的學習來源 |
| 2025-09-30 | 第一審判決 | 法院認定買賣契約成立 |
| 2026-08-12 | 第二審判決 | 廢棄原判決,認定系統通知不足以表示業者承諾 |
已確認事實、技術推論與未知事項
事件分析必須區分公開證據與架構推論,否則很容易把一個 Origin 暴露問題擴張成沒有證據支持的「主機遭入侵」。
| 證據層級 | 內容 |
|---|---|
| 公開資料可確認 | 當事人修改本機 hosts;Origin 主機未在 21:06 停止;系統接受訂單並產生通知;關閉操作後仍有訂單產生 |
| 依連線結果可合理推論 | Origin 當時可從公網到達;Web Server 能以正式 hostname 選到網站;至少一條訂單寫入路徑仍啟用 |
| 公開資料不足 | 21:06 究竟修改哪一項 Cloudflare 設定;Origin IP 如何被發現;TLS、Firewall 與 VirtualHost 的完整設定;516 筆訂單各自使用哪條連線路徑 |
| 僅屬一方主張 | DDoS 是否發生、流量規模、來源及持續時間 |
二審處理的是自動通知能否構成法律上的意思表示;工程分析處理的是另一個問題:營運方已決定停止接單,系統為何仍可完成訂單寫入及通知。法律效果與系統是否正確執行營運狀態,不能視為同一件事。
Cloudflare Reverse Proxy 與 Origin 資料流
Cloudflare DNS 記錄開啟 Proxy,也就是常說的橘雲後,訪客通常不會從 DNS 得到 Origin IP,而會得到 Cloudflare Anycast IP。一般 HTTP 流量的路徑如下:
Visitor
│ ① DNS 回傳 Cloudflare IP
▼
Cloudflare Edge
│ ② WAF、DDoS mitigation、Rate Limiting、Bot 管理、Cache
▼
VM Origin
│ ③ Web Servewr(Nginx or Apache2 or ...) 將請求交給應用程式
▼
Application → Database → Mail/Queue/Payment
這裡不是一條從瀏覽器一路延伸到 Origin 的單一連線,而是至少兩條獨立連線:
1. Visitor 與 Cloudflare Edge 之間的連線。
2. Cloudflare Edge 與 Origin 之間的連線。
Cloudflare 的 WAF、Rate Limiting 或維護規則,只能處理第一條連線確實到達 Cloudflare 的流量。若 client 直接對 Origin IP 建立 TCP 連線,Cloudflare 不在封包路徑上,自然沒有機會判斷或封鎖該請求。
這不代表 Cloudflare Proxy 沒有效果。橘雲仍能隱藏目前 DNS 回應中的 Origin IP、吸收正常路徑的攻擊流量,並在 Edge 執行安全政策;問題在於「正常 DNS 指向 Cloudflare」不等於「Origin 只接受 Cloudflare」。前者是路徑引導,後者才是存取控制。
Cloudflare 官方也要求 Origin 明確拒絕非 Cloudflare 或非可信來源流量,並建議檢查 DNS-only 記錄、郵件服務及歷史 DNS 是否暴露 Origin IP;已暴露過的 Origin IP 應評估輪替。Cloudflare:Protect your origin server
hosts 覆寫對名稱解析與連線目的地的影響
Linux 的 `/etc/hosts` 是靜態 hostname-to-IP 對照表。每一行將 IP 與一個或多個 hostname 關聯,例如:
203.0.113.10 www.example.com
203.0.113.0/24 是 RFC 5737 保留給文件範例的網段,不代表任何真實 Origin。example.com 也是保留給文件使用的網域。
Ubuntu 上的名稱解析順序由 /etc/nsswitch.conf 的 hosts: 欄位控制。常見設定會先查本機檔案,再查 DNS,但實際順序仍應以系統設定為準:
grep '^hosts:' /etc/nsswitch.conf
應用程式若使用作業系統的 resolver,便可能採用 /etc/hosts 的結果。修改通常立即生效,但應用程式或本機 resolver 若有 cache,仍可能需要重新連線或清除快取。/etc/hosts 的格式與行為可參考 Linux hosts(5) manual。
有一個常被忽略的差異:
– getent ahosts www.example.com 經過 Linux Name Service Switch,比較接近一般程式看到的解析結果。
– dig +short www.example.com 直接查詢 DNS,不會用來證明 /etc/hosts 是否生效。
因此,修改 hosts 後看到 dig 仍回覆 Cloudflare IP 並不矛盾;瀏覽器或 curl 經由系統解析時,仍可能使用本機指定的 Origin IP。
DNS、TCP、TLS 與 HTTP 的分層結果
正常路徑: www.example.com → Cloudflare IP → Cloudflare → Origin
覆寫路徑: www.example.com → Origin IP ───────────────→ Origin
| 項目 | 正常路徑 | 覆寫後 |
|---|---|---|
| URL hostname | www.example.com |
www.example.com,不變 |
| 名稱解析結果 | Cloudflare IP | 本機指定的 Origin IP |
| TCP destination | Cloudflare IP:443 | Origin IP:443 |
| TLS SNI | www.example.com |
www.example.com,不變 |
| 憑證 hostname 驗證 | 驗證 www.example.com |
仍驗證 www.example.com |
HTTP/1.1 Host |
www.example.com |
www.example.com,不變 |
HTTP/2 :authority |
www.example.com |
www.example.com,不變 |
這就是直連可能成功的關鍵:只有「目的 IP」被換掉,網站識別名稱仍然保留。
Nginx 與 Apache2 常在同一個 IP 上服務多個網站。TLS 握手時,Web Server 可依 SNI 選擇憑證與 TLS VirtualHost;進入 HTTP 後,再依 Host 或 HTTP/2 :authority 選擇網站。只要 Origin 上仍配置正式 hostname,直連請求就可能被送進正式應用程式。
直接把 URL 寫成 https://203.0.113.10/ 並不等價。此時 SNI、憑證驗證名稱及 HTTP request authority 都可能變成 IP,導致預設 VirtualHost 或憑證不符。只加 -H 'Host: www.example.com' 也不完整,因為它沒有改變 TCP destination,HTTPS 的 SNI 與憑證驗證名稱也不一定一致。
修改 hosts 本身不會:
– 改動權威 DNS 或其他使用者的解析結果。
– 取得 Shell、後台帳號或資料庫權限。
– 穿透 GCP Firewall、Nginx/Apache2 ACL 或 mTLS。
– 關閉 Cloudflare,或改變 Cloudflare 帳戶內的設定。
它只是把既有的另一個入口暴露出來。如果 Origin 已拒絕非 Cloudflare 來源,這個方法只會得到 timeout、connection refused、TLS failure 或 HTTP deny。
curl --resolve 的連線重現與封包語意
curl --resolve 可以在不修改 /etc/hosts、不需要 root 權限的情況下,為指定的 hostname:port 覆寫本次執行使用的 IP。curl 官方把它描述為命令列上的 /etc/hosts 替代方式:curl --resolve 文件。
Direct-origin 示範只使用文件 IP 或本機 loopback,用來觀察連線語意。正常 HTTPS 路徑中的 www.example.com 是欄位示例,執行前應換成自有且已開啟 Cloudflare Proxy 的 hostname。實際 Origin 測試只應在自有或明確授權的系統進行。
Step 1:區分 DNS 回應與作業系統解析結果
dig +short www.example.com
getent ahosts www.example.com
第一個命令觀察 DNS server 的回答,第二個命令觀察 Ubuntu Name Service Switch 提供給應用程式的結果。在沒有本機覆寫時,兩者通常相近;有 /etc/hosts、mDNS 或其他 NSS source 時,結果可能不同。
Step 2:觀察正常 HTTPS 連線
curl --verbose \
--noproxy '*' \
--connect-timeout 3 \
--output /dev/null \
'https://www.example.com/'
將 www.example.com 換成自有且已開啟 Cloudflare Proxy 的 hostname,verbose output 會顯示實際連線 IP、TLS 憑證及 HTTP response headers。CF-Ray 或 server: cloudflare 可作為流量經過 Cloudflare 的觀察線索,但不應單獨當成安全邊界的證明。
Step 3:保留 hostname 並覆寫 destination IP
curl --verbose \
--noproxy '*' \
--connect-timeout 3 \
--resolve 'www.example.com:443:203.0.113.10' \
--output /dev/null \
'https://www.example.com/'
這個命令表達的連線意圖是:
URL hostname = www.example.com
TCP destination = 203.0.113.10:443
TLS SNI = www.example.com
Certificate name = www.example.com
HTTP authority = www.example.com
Public DNS result = 本次連線不採用
--resolve 的第一個 hostname 必須與 URL hostname 相符,port 也必須與實際連線 port 相符。HTTPS 預設使用 443;同一 hostname 的 80 與 443 若都要測試,需要各自建立一筆 mapping。--noproxy '*' 則避免環境中的 HTTP/HTTPS Proxy 改變實際連線路徑。
範例 IP 不應出現在公共 Internet 路由,執行後很可能 timeout,這是正常結果。不要加入 -k 或 --insecure 讓命令表面成功,因為跳過 TLS 驗證會掩蓋「Origin 是否提供可信且 hostname 相符的憑證」這個成立條件。
Step 4:在 Ubuntu 本機完成可觀察驗證
終端機 A 啟動一次性 HTTP server:
python3 -m http.server 18080 --bind 127.0.0.1
終端機 B 以不存在於 DNS 的 hostname 連回本機:
curl --noproxy '*' \
--verbose \
--resolve 'shop.demo.invalid:18080:127.0.0.1' \
--output /dev/null \
'http://shop.demo.invalid:18080/'
--noproxy '*' 用來避免環境中的 HTTP Proxy 接手連線。curl output 應出現類似內容:
* Connected to shop.demo.invalid (127.0.0.1) port 18080
> Host: shop.demo.invalid:18080
第一行證明 TCP 實際連到 127.0.0.1,第二行證明 HTTP hostname 仍是 shop.demo.invalid。同一個 request 同時保有「指定 IP」與「原 hostname」,已足以重現事件所使用的連線原理。完成後在終端機 A 按 Ctrl+C 停止 server。
本機 HTTP 示範刻意不加入 TLS,目的是讓連線結果可重現且不需要建立測試憑證。真實 HTTPS Origin 還會多出 SNI、憑證信任、有效期限及 hostname 驗證,不能從 HTTP 成功直接推論 HTTPS 也會成功。
Origin Direct Access 的成立條件
修改 hosts 或使用 curl --resolve 只是選擇 destination IP。要讓請求一路進入正式應用程式,下列條件必須同時成立:
| 條件 | Origin 必須具備的狀態 | 缺少條件時的常見結果 |
|---|---|---|
| Origin IP 已知 | Client 有正確且仍有效的位址 | 連到錯誤主機或 timeout |
| 公網路由存在 | VM 有 External IP 或其他 public ingress | 無法建立連線 |
| 網路 Firewall 放行 | GCP VPC Firewall 允許該來源連入 80/443 |
TCP timeout |
| Web Server listener 放行 | Nginx/Apache2 接受該來源及 port | connection refused 或 403 |
| VirtualHost 相符 | SNI 與 request authority 能選到正式站台 | 預設站台、421 或 TLS 錯誤 |
| TLS 條件成立 | 憑證、協定及 hostname 驗證可完成 | TLS handshake 或憑證驗證失敗 |
| Application 接受操作 | 訂單 API、Session、CSRF、庫存及業務規則仍允許寫入 | 4xx/5xx,不建立訂單 |
| 下游副作用啟用 | Database、Queue、Mail、Payment 等仍執行 | 只留下部分狀態,不完成流程 |
這是一組 AND 條件,不是只要知道 IP 就一定成功。任一層正確拒絕,都能阻止事件中的完整結果。
這也說明為何 Origin IP 外洩與漏洞利用不能劃上等號。IP 是定位資訊;真正的弱點是系統仍將該 IP 當作無條件可進入的正式入口。反過來說,只隱藏 IP 也不能取代 Firewall、mTLS 或 private ingress,因為歷史 DNS、DNS-only 子網域、郵件服務、憑證紀錄或其他基礎設施都可能再次暴露 Origin。
Origin 暴露與控制面不一致
事件不是單一設定錯誤,而是 Edge、Origin 與 Application 三個層次採用不同的服務狀態。
| 層次 | 當時可觀察的狀態 | 產生的結果 |
|---|---|---|
| Cloudflare Edge | 正常訪問受到封鎖 | 多數訪客認為網站已關閉 |
| Origin ingress | 主機未停止,且至少一條公網直連路徑有效 | 請求可避開 Edge 控制 |
| Nginx/Apache2 | 正式 hostname 仍能對應網站 | Direct request 進入正式應用程式 |
| Application | 訂單 command 仍可執行 | 建立訂單資料 |
| Notification | 自動通知仍啟用 | 對外送出訂單相關訊息 |
IP 不公開不是存取控制
Origin IP 不出現在目前的 Proxied DNS 回應中,可以降低被直接發現的機率,但這是資訊隱藏,不是授權機制。可靠的控制必須讓非預期來源即使知道 IP,也無法完成連線或通過身分驗證。
若 Origin 對全 Internet 開放,Direct request 可能略過:
- Cloudflare WAF 規則。
- Rate Limiting 與 Bot 管理。
- Cloudflare DDoS mitigation。
- Edge Access policy、Redirect 或維護頁。
- 只存在 Cloudflare 端的 Cache 與 Header transformation。
能略過上述控制,不等於已取得伺服器控制權。就公開資料而言,可確認的是非預期路徑成功執行訂單;沒有足夠證據證明資料外洩、SQL injection、帳號接管、任意程式碼執行或 Shell access。較精確的技術定性是 Origin exposure 與 security control bypass,不是已證實的主機入侵。
Forwarded Header 不能自行證明來源
公開 Origin 也會影響真實訪客 IP 的信任模型。CF-Connecting-IP、X-Forwarded-For 都是 HTTP Header;任意 client 直連 Origin 時,也能自行建立同名 Header。Web Server 只有在 TCP peer 已確認是 Cloudflare IP,或連線已通過可信的 mTLS/Tunnel 身分驗證後,才能採信這些欄位。
因此,來源限制與 Real IP 還原有固定順序:先確認誰是可信 Proxy,再接受它提供的訪客 IP。單純把 CF-Connecting-IP 寫進 access log,不能證明 request 經過 Cloudflare。
服務停止的跨層控制
「網站顯示維護頁」與「系統停止接單」是兩個不同的目標。前者是呈現層結果,後者必須停止會改變狀態的 command 及其副作用。
可靠的停止程序應依下列順序執行。
Step 1:Application 拒絕新的狀態變更
建立 server-side order_acceptance_state 或 checkout_enabled,並讓所有訂單入口共用同一份權威狀態。檢查必須在 server side 執行,不能只靠前端隱藏按鈕或 JavaScript。
訂單狀態檢查與資料寫入還要處理並行競爭。概念上的 transaction 應接近:
BEGIN
lock and read order_acceptance_state
if state != OPEN: reject with ORDERING_DISABLED
validate price, promotion, stock and idempotency key
insert order with RECEIVED status
COMMIT
若先檢查、隔一段時間才寫入,切換關閉狀態的瞬間仍可能有 request 穿過檢查並在稍後 commit。使用 transaction、lock 或具相同一致性保證的設計,才能定義清楚的停止時間點。
Step 2:停止或隔離下游副作用
Database 新增一筆資料只是訂單流程的其中一步。付款、庫存保留、寄信、簡訊、出貨工作與第三方 Webhook 也必須遵守相同狀態。
緊急停止時,可將切換時間後的工作標記為 QUARANTINED,暫停 consumer 或在執行前重新檢查 incident state。已進入 Queue 的舊訊息不會因 Cloudflare 顯示維護頁而自動消失。
Step 3:Cloudflare Edge 回覆維護狀態
Application 已停止狀態變更後,再由 Cloudflare 回覆維護頁或適當的 503 Service Unavailable,降低 Origin 負載並向一般訪客提供一致訊息。若能估計恢復時間,可加上 Retry-After。
DNS 變更不適合作為唯一的緊急停止開關,因為 resolver cache、TTL、既有 keep-alive connection 及未經 DNS 的 direct path 都可能延續一段時間。
Step 4:Origin ingress 維持來源限制
GCP Firewall、Nginx/Apache2 ACL、AOP 或 Tunnel 不應只在事件發生時臨時設定。正常營運期間就應阻止非 Cloudflare direct access,維護期間則繼續維持相同限制。
Step 5:驗證正常路徑與 Origin 路徑
驗證不能只開瀏覽器看到維護頁。自有測試環境至少要確認:
- 正常 hostname 經 Cloudflare 回覆預期的維護狀態。
- 使用
curl --resolve指向自有 Origin 時,非可信來源在 TCP、TLS 或 HTTP 層被拒絕。 - 繞過前端畫面的訂單 API 仍由 Application 回覆
ORDERING_DISABLED,且 Database 沒有新增訂單。 - Queue、Mail、Payment 與庫存沒有產生新的副作用。
- Cloudflare、Origin、Application、Database 與 Worker log 能以 request ID 或 order ID 對應。
驗證 curl --resolve 時只應對自有或書面授權的 Origin 執行,並使用不會改變正式資料的 health endpoint 或 staging environment。
Step 6:保留證據與設定版本
事件期間應保存 Cloudflare Security Events、Audit Log、Origin access/error log、Application log、Database audit、Queue 記錄與設定變更時間。敏感 Cookie、Authorization、付款資料與個資不得直接寫入一般 log。
沒有跨層時間戳與 request ID,只能知道「曾經有訂單」,無法可靠回答 request 經過哪條路徑、在哪個控制點被接受,以及 516 筆訂單中哪些與 direct access 有關。
Cloudflare Origin 防護方案與實作範圍
Origin 保護沒有一個設定能取代所有其他層。不同控制回答的是不同問題:
| 控制 | 解決的問題 | 不解決的問題 |
|---|---|---|
| Proxied DNS | 讓一般流量先到 Cloudflare,隱藏目前 DNS 回應中的 Origin IP | 不強制 Origin 拒絕直連 |
| Cloudflare IP allowlist | 在 GCP 或 Web Server 只允許 Cloudflare 網段連入 | 不驗證特定 Cloudflare 帳戶;不停止 Application 副作用 |
| Authenticated Origin Pulls(AOP) | Origin 以 client certificate 驗證連線來自 Cloudflare | 不取代 Application 授權或 kill switch |
| Cloudflare Tunnel | Origin 主動向 Cloudflare 建立 outbound-only connection,不保留 public inbound listener | 需要安裝及維運 cloudflared;不與 AOP 疊加 |
| Full (strict) | Cloudflare 驗證 Origin certificate 的信任、期限及 hostname | 不驗證直連 Origin 的 client 是 Cloudflare |
| Application kill switch | 停止訂單、付款、寄信等業務副作用 | 不提供 DDoS 或網路層保護 |
GCP 單一 VM 的建議基線
以「Cloudflare → GCP Compute Engine VM → Nginx/Apache2」的簡單架構來說,可採用以下組合:
- DNS 記錄啟用 Cloudflare Proxy。
- 已公開過的 Origin IP 評估輪替,並移除會暴露同一 IP 的 DNS-only 記錄或共用郵件服務。
- GCP VPC Firewall 的
80/443只允許 Cloudflare 官方 IPv4、IPv6 CIDR;沒有使用 IPv6 時,確認 VM、DNS 與 listener 都沒有額外 IPv6 入口。 - Nginx/Apache2 只配置必要 hostname,未知
Host不送進正式 Application。 - Cloudflare 到 Origin 使用 Full (strict),讓 Cloudflare 驗證 Origin certificate。
- 需要更強的 Origin 身分驗證時,加入 zone-level 或 per-hostname AOP 自有憑證;Cloudflare 提供的 Global AOP 憑證只能證明 request 來自 Cloudflare network,不能證明來自特定帳戶。
- 若不需要 public ingress,改用 Cloudflare Tunnel,從架構上移除可被直連的公開 listener。Tunnel 已使用 connector credential 驗證連線,與 AOP 的工作模式不同。
- Application 保留獨立 kill switch,並讓訂單、付款、Queue 及通知共用相同 incident state。
- Access log 同時保留實際 peer IP 與通過 Trusted Proxy 驗證後的 visitor IP,避免事後只剩無法還原路徑的單一欄位。
- 從外部網路定期執行安全的 direct-origin negative test,確認未授權路徑持續失敗。
Cloudflare 將 IP allowlist 列為網路層 Origin 防護,並要求明確封鎖所有非 Cloudflare 或非可信來源流量;AOP 則在 Full/Full (strict) 之上增加 client certificate 驗證。Cloudflare Origin 防護文件、Authenticated Origin Pulls 文件。
Full (strict) 的方向恰好相反:它是 Cloudflare 作為 client 時驗證 Origin server certificate,要求憑證未過期、由公開 CA 或 Cloudflare Origin CA 簽發,且 CN/SAN 符合 hostname。它保護 Cloudflare-to-Origin TLS,不是 Origin ingress allowlist。Cloudflare:Full (strict)
Origin 若只提供 Cloudflare Origin CA certificate,一般瀏覽器或未加入該 CA 的 curl 直連時通常會因不信任簽發者而失敗;這仍不是來源限制。網路入口依然存在,Origin 也沒有藉此驗證連入的 client 是不是 Cloudflare,因此不能用 client 端預設不信任取代 Firewall、AOP 或 Tunnel。
喜傑獅事件的技術結論可以濃縮成一句話:Edge 顯示關閉,不代表 Origin 與 Application 已停止。 完整防護必須同時限制 Origin 來源、驗證可信 Proxy、保護 Cloudflare-to-Origin TLS,並由 Application 掌握真正的業務停止狀態。即使 Origin IP 再次被發現,系統也不應因此多出一條可完成交易的未授權路徑。