PubliclyAccessible=true 不等於對外曝露:同一台機器,RDS 連不到、kubectl 卻通

起因:一個旗標,讓 AI 一秒喊出了錯的資安結論 那天清掉一顆「砍了 EC2(Elastic Compute Cloud,AWS 的雲端虛擬機器)卻忘了收」的閒置 Elastic IP(彈性 IP — AWS 可保留、可重複指派的固定公網位址)後,我順手請我的 AI 助手盤點帳號還開著哪些服務。掃到一台正式環境的 RDS(Relational Database Service,AWS 託管的關聯式資料庫)PostgreSQL,設定裡寫著 PubliclyAccessible: true。 AI 第一個反應就回我:「正式資料庫直接掛公網,有資安風險。」這句話聽起來很合理——但它是錯的。真相要等到實際讀了 Security Group 規則、再從本機跑一次連線測試才浮現。 這篇文章講的就是這個落差:一個旗標的字面意思,跟它實際造成的曝露,是兩回事。 也順帶示範——在「AI 已經會幫你讀設定、寫指令」的年代,為什麼這種誤判依然會發生。 先說清楚:Security Group 是什麼 Security Group(安全群組,以下簡稱 SG)是 AWS 的虛擬防火牆,掛在資源的網路介面 ENI(Elastic Network Interface,彈性網路介面)上。它有三個關鍵性質: 預設全部拒絕:沒有明確放行的流量一律擋掉。你只能「加白名單」,沒有「黑名單」這種東西。 來源可以是 IP 網段,也可以是另一個 SG:後者意思是「凡是掛了某個 SG 的資源一律放行」,不必管它的 IP 是多少——這在 VPC(Virtual Private Cloud,虛擬私有雲)內部互連時非常好用。 stateful(狀態感知 — 放行了進來的連線,回程流量自動允許):不必另外開一條出站規則。 SG 的逐步設定我在 如何使用 psql 連線 AWS RDS 那篇有完整圖解(「SG 就像 RDS 的防火牆」)。這裡只談它跟「公開存取」旗標之間,那個最容易被誤會的互動。 旗標的陷阱:PubliclyAccessible=true 只給門牌,不給鑰匙 關鍵誤解就在這裡。PubliclyAccessible 這個旗標只決定一件事:RDS 要不要拿到一個公網 IP,以及一個可從網際網路解析的 DNS(網域名稱)。它完全不決定「誰連得進來」。 ...

June 25, 2026 · 3 分鐘 · Peter

Fiverr 假接案陷阱:拆解藏在國旗 SVG 裡的 Node.js 後門

前言:一個「幫我看看 repo」的接案邀請 接案平台上來了一個看似正常的全端案子:一個既有的 Next.js + Node.js 網站,要我接手完成。對方很客氣地寄來 GitHub repo 邀請,請我「先看看 homepage、評估報價與里程碑」。 這是再普通不過的接案流程。但有一個習慣救了我:任何陌生的 repo,我都先靜態審查,絕不先 npm install 跑起來。 這次掃下去,在一個名字無害到不行的檔案 server/lib/serverStartup.js 裡,我看到了這輩子最不想在「客戶專案」裡看到的東西——eval()。 順著這條線往下挖,是一套設計相當精巧的隱寫術(steganography)後門:惡意碼完全不在 .js 檔裡,而是藏在 21 個國旗 SVG 圖檔的註解中。這篇文章完整拆解它的三層結構、實際行為,以及背後那套針對開發者的社交工程攻擊。 ⚠️ 本文所有惡意網域均以資安界慣例 defang(example[.]dev)處理,程式碼片段皆為去活化的分析用途,不含可直接執行的下載指令。 攻擊全貌:社交工程 + 隱寫術 + 多階段載入 這類攻擊在業界稱為 Contagious Interview(假面試/假接案),核心不是去駭你的伺服器,而是騙你親手把惡意碼跑在自己的開發機上。一個 npm run dev 就中。 整條感染鏈長這樣: flowchart TD A[接案邀請<br/>GitHub repo] --> B[npm run dev<br/>啟動 server] B --> C[env.js 載入時<br/>呼叫 runServerStartupLogs] C --> D[serverStartup.js<br/>eval Check validation] D --> E[validation 讀取<br/>21 個國旗 SVG 註解] E --> F[串接 + base64 解碼<br/>= 載入器] F --> G[回報主機 + VM 偵測<br/>拉取各竊取模組] G --> M1[剪貼簿挾持<br/>clipper] G --> M2[瀏覽器錢包<br/>+ 憑證竊取] G --> M3[檔案 / 密鑰<br/>外洩模組] G --> M4[socket.io<br/>遠端 shell RAT] classDef bait fill:#d1ecf1,stroke:#17a2b8,color:#0c5460 classDef trap fill:#fff3cd,stroke:#ffc107,color:#856404 classDef evil fill:#f8d7da,stroke:#dc3545,color:#721c24 class A,B bait class C,D,E,F,G trap class M1,M2,M3,M4 evil 精巧之處有兩層。第一層是偽裝:每個檔案單獨看都「像正常程式」——檔名叫 serverStartup、validation,函式做的事看起來是「讀國旗檔做驗證」。第二層是模組化:載入器本身很小,真正的惡意能力被拆成數個可獨立拉取的竊取模組,這正是 Contagious Interview(針對開發者的假面試/假接案)攻擊家族的典型架構。只有把整條鏈串起來,才看得出它是一套完整的竊取工具包。 ...

June 4, 2026 · 5 分鐘 · Peter

Strapi 忘記密碼的安靜回應:Anti-Enumeration、Phishing-as-a-Service 與撞庫經濟學

問題現場:一個「成功」卻收不到信的忘記密碼 某個週一下午,QA 回報:在一個 Flutter + Strapi 架構的 App 上,用測試帳號 [email protected] 點「忘記密碼」,畫面跳出 Success: Reset password link sent successfully on your email account.,但信箱(包含垃圾郵件匣)一封信都沒有。 直覺先排除幾個明顯可能:SMTP 設定壞掉、用戶被 block、信件被 Gmail 擋。但真正的答案比這些都有趣 — 這不是 bug,是 Strapi 一個刻意的安全設計,只是 App 前端把它翻譯錯了。 kubectl 診斷:20ms 與 800ms 的兩種人生 進 Strapi pod 撈 log,找 forgot-password 請求: kubectl logs -n default strapi-prod-xxxxxxxxx-xxxxx --since=6h \ | grep -E 'forgot-password|Reset password URL' 抽出的關鍵幾行: 04:04:52 info: Reset password URL generated: https://example.com/... 04:04:53 http: POST /api/auth/forgot-password (948 ms) 200 05:15:44 http: POST /api/auth/forgot-password (20 ms) 200 05:16:53 info: Reset password URL generated: https://example.com/... 05:16:54 http: POST /api/auth/forgot-password (664 ms) 200 05:17:30 http: POST /api/auth/forgot-password (26 ms) 200 一眼看出兩種節奏:800ms 級別的請求伴隨「Reset password URL generated」log,而 20-30ms 的請求則完全沒有。同一支 API,為什麼耗時差 30 倍? ...

April 21, 2026 · 4 分鐘 · Peter

在 Kubernetes 上部署 OV SSL 證書:完整實戰指南

為什麼選擇 TWCA OV 證書 在生產環境中,使用自簽憑證會導致瀏覽器顯示不安全警告,影響使用者信任。雖然 Let’s Encrypt 提供免費的自動化證書,但某些企業或政府專案需要台灣在地的認證機構簽發證書以符合法規要求。 SSL 證書的三個等級 SSL 證書依據驗證強度分為三個等級: 證書類型 驗證內容 適用場景 信任標記 價格 DV (Domain Validation) 僅驗證網域所有權 個人網站、部落格 🔒 基本鎖頭 免費~低 OV (Organization Validation) 驗證組織身份 企業網站、SaaS 服務 🔒 組織名稱 中 EV (Extended Validation) 嚴格驗證企業 金融、電商、政府 🟢 綠色網址列 高 本文使用的是 TWCA OV SSL 證書,它提供: ✅ 組織身份驗證(瀏覽器可顯示公司名稱) ✅ 完整的證書鏈(受所有主流瀏覽器信任) ✅ 12 個月有效期 ✅ 台灣在地技術支援 HTTPS 與 SSL/TLS 運作原理 在深入部署之前,先理解 HTTPS 如何保護資料傳輸: sequenceDiagram participant Browser as 瀏覽器<br/>(內建 TWCA 根憑證) participant Server as Web Server Note over Browser,Server: 1. TLS 握手階段 Browser->>Server: ClientHello (支援的加密套件) Server->>Browser: ServerHello (選擇的加密套件) Server->>Browser: 傳送證書鏈<br/>(伺服器證書 + 中繼憑證) Note over Browser: 2. 證書驗證(完全離線) Browser->>Browser: 用中繼憑證驗證伺服器證書 Browser->>Browser: 用內建根憑證驗證中繼憑證 Browser->>Browser: 檢查證書有效期與網域 Note over Browser: ✓ 證書有效 Note over Browser,Server: 3. 金鑰交換 Browser->>Server: 用公鑰加密的 session key Server->>Server: 用私鑰解密 session key Note over Browser,Server: 4. 加密通訊 Browser->>Server: 加密的 HTTP 請求 Server->>Browser: 加密的 HTTP 回應 關鍵機制說明: ...

December 10, 2025 · 7 分鐘 · Peter

解決 Strapi CMS 正式環境空白頁的踩坑經驗分享

前言:一個簡單的環境變數引發的災難 在部署 Strapi CMS 到 Kubernetes 正式環境時,只是加了一行看似無害的環境變數設定: env: - name: NODE_ENV value: production # 就是這一行! 結果卻導致整個管理後台變成一片空白,連登入頁面都看不到。更詭異的是: ✅ API 完全正常,GraphQL 和 REST 都能回應 ✅ Pod 狀態正常,沒有任何錯誤訊息 ✅ 日誌顯示 Strapi 成功啟動 ❌ 瀏覽器打開 /admin 卻是一片空白 這種「Schrodinger 的服務」(同時正常又不正常)讓人抓狂。經過一番排查,終於發現罪魁禍首是 CSP (Content Security Policy) 在作怪。 本文將深入探討: 為什麼正式環境會出現空白頁 CSP 的工作原理與安全機制 完整的問題排查步驟 如何正確配置 Strapi 的安全策略 生產環境的安全最佳實踐 問題背景:開發正常,正式環境空白 環境差異對比 graph LR subgraph "開發環境 (Development)" D1[NODE_ENV=development] D1 --> D2[✅ CSP 寬鬆] D1 --> D3[✅ 詳細錯誤訊息] D1 --> D4[✅ Hot Reload] D2 --> D5[✅ Admin Panel 正常] end subgraph "正式環境 (Production)" P1[NODE_ENV=production] P1 --> P2[🔒 CSP 嚴格] P1 --> P3[⚠️ 精簡錯誤訊息] P1 --> P4[📦 壓縮打包] P2 --> P5[❌ Admin Panel 空白] end style D5 fill:#22c55e style P5 fill:#ef4444 問題現象詳細描述 Kubernetes Deployment 設定: ...

May 7, 2025 · 9 分鐘 · Peter