BlogEngine Extension: Secure Post v1.0

Case #1: 以 Extension 實作「單篇貼文密碼保護」以完成系統切換

Problem Statement(問題陳述)

業務場景:家中大人要求在新部落格系統(BlogEngine.NET)中,特定文章需輸入密碼才能觀看,且不需建立帳號或登入。舊系統使用 CommunityServer 2007,若新系統無此能力則拒絕切換。需求強調「簡單、快速、少維護」,只對少量貼文啟用保護。 技術挑戰:BlogEngine 當下沒有現成的「單篇密碼保護」功能;現有擴充以使用者/角色和分類授權為主,不符合免帳號與逐篇控制的需求。 影響範圍:若不解決,系統無法遷移;內容失控造成隱私外洩;管理者需額外維護帳號體系。 複雜度評級:中

Root Cause Analysis(根因分析)

直接原因:

  1. BlogEngine 既有擴充以角色/分類授權為主,缺乏逐篇密碼保護。
  2. 先前 IIS 整合式驗證導致錯誤授權與高維護成本。
  3. 無現成 UI 支援快速指定「哪些貼文要保護」。

深層原因:

  • 架構層面:BlogEngine 授權設計對使用者/角色友好,但缺乏細粒度的 per-post 密碼策略。
  • 技術層面:未提供簡單的 server-side 攔截點供動態替換內容。
  • 流程層面:以伺服器/IIS 層面配置整站權限,無法滿足單篇貼文的差異化需求。

Solution Design(解決方案設計)

解決策略:撰寫 SecurePost Extension,於 Post.Serving 事件攔截輸出;若貼文標記為保護(內容以 [password] 開頭)且未通過密碼驗證,改為輸出提示與輸入框;若已登入或密碼正確,原樣輸出。透過 Extension Manager 提供訊息、提示、密碼三項設定。

實施步驟:

  1. 掛載輸出事件與比對開關
    • 實作細節:static constructor 中註冊 Post.Serving;在事件處理中檢查 [password] 開頭與登入狀態。
    • 所需資源:BlogEngine.Core、.NET 2.0/3.5
    • 預估時間:0.5 小時
  2. 密碼檢查與 UI 提示
    • 實作細節:以 Request[“pwd”] 比對設定密碼;不通過則輸出提示與輸入欄位,通過則放行。
    • 所需資源:HttpContext、StringBuilder
    • 預估時間:0.5 小時

關鍵程式碼/設定:

// 掛事件並實作攔截
[Extension("SecurePost", "1.0", "<a href=\"http://columns.chicken-house.net\">chicken</a>")]
public class SecurePost
{
    static SecurePost()
    {
        Post.Serving += new EventHandler<ServingEventArgs>(Post_Serving);
    }

    private static void Post_Serving(object sender, ServingEventArgs e)
    {
        var post = sender as Post;

        // 已登入者直接放行
        if (HttpContext.Current.User.Identity.IsAuthenticated) return;

        // 密碼正確放行
        if (HttpContext.Current.Request["pwd"] == Password) return;

        // 僅當貼文以 [password] 開頭時啟動保護
        if (!e.Body.StartsWith("[password]", StringComparison.CurrentCultureIgnoreCase)) return;

        // 替換輸出為提示+輸入框
        var bodySB = new StringBuilder();
        bodySB.AppendFormat("<b>{0}</b><p/>", HtmlEncode(SecurePostMessage));
        if (e.Location != ServingLocation.Feed)
        {
            bodySB.AppendFormat(
                @"請輸入密碼(提示: <b>{0}</b>): <input id=""postpwd"" type=""password""/>" +
                @"<button onclick=""document.location.href='{1}'+'?pwd='+escape(this.parentNode.all.postpwd.value);"">GO</button>",
                PasswordHint, post.AbsoluteLink);
        }
        e.Body = bodySB.ToString();
    }
}

實際案例:本篇 Secure Post v1.0 Extension;在管理端 Extension Manager 可見並可編輯三項設定。 實作環境:BlogEngine.NET 1.x、ASP.NET WebForms、C#、.NET Framework 2.0/3.5、IIS 6/7。 實測數據:

  • 改善前:無逐篇密碼保護;僅有 IIS/角色授權可選,需求未被滿足。
  • 改善後:< 100 行 C# 完成;1 檔案部署;3 項設定可視化。
  • 改善幅度:功能覆蓋度 0% → 100%;部署步驟縮減為單檔投放。

Learning Points(學習要點) 核心知識點:

  • 使用 BlogEngine Post.Serving 事件攔截輸出。
  • 以 server-side 邏輯替換內容避免前端洩漏。
  • 用 Extension Manager 管理設定。

技能要求:

  • 必備技能:C#、ASP.NET、BlogEngine 擴充基礎。
  • 進階技能:基本安全設計、UI/UX 提示設計。

延伸思考:

  • 可否支援每篇不同密碼(per-post metadata)?
  • QueryString 明碼的風險與緩解方式?
  • 如何將提示與按鈕以控件或主題佈景整合?

Practice Exercise(練習題)

  • 基礎練習:建立一個最小 Extension,於 Post.Serving 將內容加上固定前綴。
  • 進階練習:加入 [password] 標記判斷與訊息提示。
  • 專案練習:完成可透過設定頁改變提示文字與密碼的完整方案。

Assessment Criteria(評估標準)

  • 功能完整性(40%):可逐篇保護並正確放行/阻擋。
  • 程式碼品質(30%):事件掛載、早退邏輯清晰,無重複程式碼。
  • 效能優化(20%):未保護貼文成本極低;字串操作高效。
  • 創新性(10%):良好 UX 或更彈性的啟用策略。

Case #2: 以 Server-Side 驗證避免「檢視原始碼」洩密

Problem Statement(問題陳述)

業務場景:讀者不必登入,只需輸入密碼即可閱讀受保護內容;關鍵是避免未授權者透過「檢視原始碼」取得密碼或內容。 技術挑戰:許多開發者習慣用 CSS/JS 將內容「隱藏」,但原始碼仍可見;需以伺服器端把關。 影響範圍:若處理不當,保護機制形同虛設,導致敏感資訊外洩。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 將內容僅在前端隱藏(DHTML/CSS)會在 HTML 中直出。
  2. 在前端嵌入密碼字串易被檢視原始碼發現。
  3. 缺少伺服器端驗證的最小保護原則。

深層原因:

  • 架構層面:輸出管線未設置保護關卡。
  • 技術層面:缺乏 server-side content gating。
  • 流程層面:對前端隱藏與真正存取控制的界線不清。

Solution Design(解決方案設計)

解決策略:建立「兩原則」—(1) 密碼只在伺服器端比對;(2) 未通過驗證前,絕不將貼文內容送給瀏覽器。以 Post.Serving 事件中比對 Request[“pwd”] 與設定密碼,不通過即替換輸出為提示畫面。

實施步驟:

  1. 伺服器端密碼比對
    • 實作細節:if (Request[“pwd”] == Password) return; 否則不輸出原文。
    • 所需資源:HttpContext、ExtensionSettings
    • 預估時間:0.3 小時
  2. 未通過即改寫輸出
    • 實作細節:以 StringBuilder 組合提示 HTML,e.Body = bodySB.ToString()。
    • 所需資源:StringBuilder
    • 預估時間:0.3 小時

關鍵程式碼/設定:

if (HttpContext.Current.Request["pwd"] == Password)
{
    // 通過:保持原內容
    return;
}

// 未通過:覆寫輸出為提示
var bodySB = new StringBuilder();
bodySB.AppendFormat("<b>{0}</b><p/>", HtmlEncode(SecurePostMessage));
e.Body = bodySB.ToString();

實作環境:BlogEngine.NET 1.x、C#、ASP.NET WebForms。 實測數據:

  • 改善前:受保護內容仍可於 HTML 原始碼中看見(若只用 DHTML)。
  • 改善後:原文不被送達瀏覽器;HTML 內僅有提示與輸入框。
  • 改善幅度:洩漏風險顯著下降(從可見 → 不可見)。

Learning Points(學習要點) 核心知識點:

  • 前端隱藏≠存取控制。
  • server-side content gating 是最低限度的保護。
  • 覆寫輸出是 WebForm 可行策略。

技能要求:

  • 必備技能:C#、HTTP 基礎。
  • 進階技能:輸出管線與安全威脅模型。

延伸思考:

  • 可改用 POST 與 Session 減少 URL 洩漏?
  • 加上速率限制/鎖定機制防暴力嘗試?
  • 以短期 token 在伺服器端緩存通過狀態?

Practice Exercise(練習題)

  • 基礎練習:在伺服器端以條件覆寫輸出。
  • 進階練習:將通過狀態存入 Session,避免重複輸入。
  • 專案練習:完成以 POST 提交密碼的版本。

Assessment Criteria(評估標準)

  • 功能完整性(40%):未通過不得見原文。
  • 程式碼品質(30%):條件與早退清晰。
  • 效能優化(20%):未通過時最小輸出。
  • 創新性(10%):防暴力與體驗兼顧。

Case #3: 以內容標記 [password] 作為啟用開關

Problem Statement(問題陳述)

業務場景:僅少數貼文需要保護;作者希望不必在後台逐筆設定,而能以最簡方式標示哪些貼文要保護。 技術挑戰:無 per-post 設定欄位;需在不改核心與資料庫前提下做到逐篇啟用/停用。 影響範圍:節省作者維護時間;避免全站誤保護。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. BlogEngine 當下無內建「受保護」欄位。
  2. 僅有 Extension 層的全域設定。
  3. 需要極簡化的「逐篇」開關。

深層原因:

  • 架構層面:缺 meta 標記欄位。
  • 技術層面:可行策略是內容掃描。
  • 流程層面:作者偏好以內文標示避免多處設定。

Solution Design(解決方案設計)

解決策略:比照 BreakPost 擴充做法,若 e.Body 以 [password] 開頭,即視為需保護。這是一種無侵入、零資料庫的啟用策略。

實施步驟:

  1. 比對內容開頭
    • 實作細節:e.Body.StartsWith(“[password]”, StringComparison.CurrentCultureIgnoreCase)
    • 所需資源:.NET 字串 API
    • 預估時間:0.2 小時
  2. 僅對命中貼文啟動攔截
    • 實作細節:未命中則 return;命中則改寫輸出或進入驗證流程。
    • 所需資源:Post.Serving 事件
    • 預估時間:0.2 小時

關鍵程式碼/設定:

if (!e.Body.StartsWith("[password]", StringComparison.CurrentCultureIgnoreCase))
{
    // 未標記:不啟動保護
    return;
}

// 標記存在:進行密碼 gating

實作環境:BlogEngine.NET 1.x、C#。 實測數據:

  • 改善前:需透過後台或資料庫逐篇設定(不可行)。
  • 改善後:在開頭加上 [password] 即可;0 資料庫修改。
  • 改善幅度:逐篇設定時間大幅縮減(單貼文 < 5 秒)。

Learning Points(學習要點) 核心知識點:

  • 以內容標記作為切換器的權衡。
  • Culture-aware、忽略大小寫比對。
  • 無侵入式啟用策略。

技能要求:

  • 必備技能:C# 字串處理。
  • 進階技能:內容管線設計與可維護性。

延伸思考:

  • 是否支援自訂標記(設定化)?
  • 如需隱藏標記,是否在放行後移除它?
  • 是否支援 markdown/front-matter 方式?

Practice Exercise(練習題)

  • 基礎:寫一個偵測 [secret] 標記的小功能。
  • 進階:於放行後自動移除標記輸出。
  • 專案:支持以設定指定不同啟用標記。

Assessment Criteria(評估標準)

  • 功能完整性(40%):僅標記貼文啟動保護。
  • 程式碼品質(30%):比對與早退簡潔。
  • 效能(20%):比對成本低。
  • 創新(10%):標記可配置化。

Case #4: 已登入者免密碼的白名單放行

Problem Statement(問題陳述)

業務場景:管理者/作者已登入後台時,不希望還要輸入密碼才能閱讀自己的草稿或受保護文章。 技術挑戰:需在同一攔截點區分「已驗證使用者」與一般訪客,避免反覆驗證。 影響範圍:若無白名單放行,作者體驗差、工作效率下降。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 預設所有人都被攔截會干擾管理流程。
  2. BlogEngine 已提供 Identity.IsAuthenticated 可用。
  3. 未適當判斷登入狀態會造成作者阻塞。

深層原因:

  • 架構層面:現有登入機制應與擴充共用。
  • 技術層面:需在最前面早退避免多餘處理。
  • 流程層面:編輯與檢視流程需要低摩擦。

Solution Design(解決方案設計)

解決策略:在 Post_Serving 的首個條件加入 HttpContext.Current.User.Identity.IsAuthenticated 檢查,已登入者直接 return,保留原始輸出。

實施步驟:

  1. 置頂認證判斷
    • 實作細節:if (User.Identity.IsAuthenticated) return;
    • 所需資源:HttpContext
    • 預估時間:0.1 小時
  2. 維持後續流程
    • 實作細節:未登入者才進入密碼比對與內容改寫。
    • 所需資源:既有程式碼
    • 預估時間:0.1 小時

關鍵程式碼/設定:

if (HttpContext.Current.User.Identity.IsAuthenticated)
{
    // 已登入:不打擾編輯/管理者
    return;
}

實作環境:BlogEngine.NET 1.x、C#。 實測數據:

  • 改善前:作者需重複輸入密碼查看自己的文章。
  • 改善後:作者無需輸入;訪客仍需密碼。
  • 改善幅度:作者操作步驟減少至 0(每次檢視)。

Learning Points(學習要點) 核心知識點:

  • 白名單/繞過策略設計。
  • 早退(early return)最佳實務。
  • 使用既有身分驗證。

技能要求:

  • 必備技能:C#、ASP.NET 身分物件。
  • 進階技能:流程設計與 UX 思維。

延伸思考:

  • 可否擴展為角色(例如 Editors)也免輸入?
  • 前後台不同主機/子域名時的登入態分享問題。
  • 如何記錄繞過決策以便稽核?

Practice Exercise(練習題)

  • 基礎:加入登入放行邏輯。
  • 進階:加入角色放行;僅特定角色免密碼。
  • 專案:提供設定開關,允許管理者選擇是否放行登入者。

Assessment Criteria(評估標準)

  • 功能完整性(40%):登入者確實放行。
  • 程式碼品質(30%):條件順序合理。
  • 效能(20%):早退減少後續成本。
  • 創新(10%):彈性角色策略。

Case #5: 以 QueryString 傳遞密碼的極簡互動

Problem Statement(問題陳述)

業務場景:無需建立表單或複雜後端控制器,只要讓讀者輸入密碼即可直達內容頁。 技術挑戰:在不引入額外頁面/控制器的前提下,如何將使用者輸入值傳回伺服器進行比對。 影響範圍:縮短開發時程,但可能導致 URL 暴露密碼的風險。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 不想引入新頁面或後端處理 POST。
  2. 需用最少程式碼完成互動。
  3. 密碼若置於 URL,將出現在瀏覽記錄/Referer。

深層原因:

  • 架構層面:無自建控制器的擴充模式。
  • 技術層面:採用 GET redirect 可立即回到原文。
  • 流程層面:偏好單頁交互體驗。

Solution Design(解決方案設計)

解決策略:生成一個輸入框與 GO 按鈕,按下後以 JavaScript 將頁面導向 post.AbsoluteLink + “?pwd=” + escape(value);伺服器端以 Request[“pwd”] 比對。

實施步驟:

  1. 客端導向傳值
    • 實作細節:button onclick 事件組合 URL。
    • 所需資源:簡單 JavaScript
    • 預估時間:0.2 小時
  2. 伺服端讀取比對
    • 實作細節:Request[“pwd”] 與設定密碼比對。
    • 所需資源:HttpContext
    • 預估時間:0.1 小時

關鍵程式碼/設定:

bodySB.AppendFormat(
  @"請輸入密碼(提示: <b>{0}</b>): <input id=""postpwd"" type=""password""/>" +
  @"<button onclick=""document.location.href='{1}'+'?pwd='+escape(this.parentNode.all.postpwd.value);"">GO</button>",
  PasswordHint, post.AbsoluteLink);

實作環境:BlogEngine.NET 1.x、C#。 實測數據:

  • 改善前:需建立額外表單頁或伺服器端處理流程。
  • 改善後:零額外頁面;以 GET 即可傳回密碼。
  • 改善幅度:開發與部署時間明顯縮短(單功能 < 0.5 小時)。

Learning Points(學習要點) 核心知識點:

  • GET 參數傳遞與安全取捨。
  • 以最小成本完成互動流程。
  • URL 編碼(escape)的用途與限制。

技能要求:

  • 必備技能:JavaScript 基礎、ASP.NET Request。
  • 進階技能:安全性評估(Referer/歷史紀錄風險)。

延伸思考:

  • 改以 POST 提交降低 URL 暴露?
  • 使用 Session/Cookie 記錄通過狀態?
  • 以一次性 token 代替明碼?

Practice Exercise(練習題)

  • 基礎:將密碼改以 POST 提交。
  • 進階:加入「顯示/隱藏密碼」按鈕與鍵盤 Enter 觸發。
  • 專案:完成 GET/POST 雙模式並可設定切換。

Assessment Criteria(評估標準)

  • 功能完整性(40%):能將輸入帶回伺服器並判斷。
  • 程式碼品質(30%):前端事件與編碼正確。
  • 效能(20%):無多餘往返。
  • 創新(10%):安全性強化設計。

Case #6: 透過 Extension Manager 配置訊息、提示與密碼

Problem Statement(問題陳述)

業務場景:非開發者需能在後台輕鬆設定顯示訊息、密碼提示與真正密碼;避免改檔案或資料庫。 技術挑戰:以 Extension 層提供可視化設定與預設值,並在程式內讀取使用。 影響範圍:提升可維護性與操作便利性,降低支援成本。 複雜度評級:中

Root Cause Analysis(根因分析)

直接原因:

  1. 沒有設定 UI 將迫使使用者改程式。
  2. 自行寫檔或 DB 增加複雜度。
  3. 無預設值會降低開箱體驗。

深層原因:

  • 架構層面:BlogEngine 已提供 ExtensionSettings API。
  • 技術層面:需正確設置 IsScalar 與 Import/GetSettings 流程。
  • 流程層面:設定與程式分離,易於管理。

Solution Design(解決方案設計)

解決策略:在 static constructor 中建立 ExtensionSettings,加入三個參數與預設值,設為 IsScalar,再由 ExtensionManager.ImportSettings 與 GetSettings 讀取。

實施步驟:

  1. 定義參數與預設值
    • 實作細節:AddParameter、AddValues、IsScalar、Help。
    • 所需資源:ExtensionSettings
    • 預估時間:0.3 小時
  2. 載入設定供程式使用
    • 實作細節:_settings = ExtensionManager.GetSettings(“SecurePost”)
    • 所需資源:ExtensionManager
    • 預估時間:0.2 小時

關鍵程式碼/設定:

var settings = new ExtensionSettings("SecurePost");
settings.AddParameter("SecurePostMessage", "顯示訊息:");
settings.AddParameter("PasswordHint", "密碼提示:");
settings.AddParameter("PasswordValue", "指定密碼:");
settings.AddValues(new[] {
    "本篇文章已受密碼保護,請依照題示輸入密碼。", 
    "一二三四", "1234" });
settings.IsScalar = true;
settings.Help = "用密碼保護文章的內容。";
ExtensionManager.ImportSettings(settings);
_settings = ExtensionManager.GetSettings("SecurePost");

實作環境:BlogEngine.NET 1.x、C#。 實測數據:

  • 改善前:需修改原始碼才能改密碼/訊息。
  • 改善後:後台 UI 可視化調整 3 項設定。
  • 改善幅度:非工程師可自助完成設定(支援工時降為 0)。

Learning Points(學習要點) 核心知識點:

  • ExtensionSettings/ExtensionManager 使用。
  • IsScalar 單一設定組合。
  • 預設值與 Help 增進可用性。

技能要求:

  • 必備技能:C#、BlogEngine 擴充 API。
  • 進階技能:設定版本/遷移策略。

延伸思考:

  • 是否支援多組設定(多密碼)?
  • 設定變更是否需快取或即時生效?
  • 需否審計設定變動?

Practice Exercise(練習題)

  • 基礎:新增一項設定(例如按鈕文字)。
  • 進階:支援多組密碼(IsScalar = false)。
  • 專案:製作完整設定頁包含驗證與說明文件。

Assessment Criteria(評估標準)

  • 功能完整性(40%):設定可讀寫並生效。
  • 程式碼品質(30%):設定讀寫處理清晰。
  • 效能(20%):設定讀取不產生瓶頸。
  • 創新(10%):設定體驗優化。

Case #7: 以 HtmlEncode 避免訊息插入造成 XSS

Problem Statement(問題陳述)

業務場景:顯示訊息與提示可能由使用者設定,若未編碼直接輸出,可能導致跨站腳本(XSS)。 技術挑戰:在組合 HTML 時正確編碼可變字串,同時維持必要的標記顯示。 影響範圍:XSS 將危及所有讀者;是重大安全風險。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 未對使用者提供之設定文字做編碼。
  2. 直接以 StringBuilder 拼接 HTML。
  3. 缺少輸出層安全控管。

深層原因:

  • 架構層面:無統一的輸出編碼層。
  • 技術層面:HtmlEncode 未被普遍使用。
  • 流程層面:設定資料未被視為不可信輸入。

Solution Design(解決方案設計)

解決策略:在所有動態插入點使用 HtmlEncode,僅對確定安全的固定 HTML 保持原樣;必要時建立小工具方法統一處理。

實施步驟:

  1. 對訊息編碼
    • 實作細節:HtmlEncode(SecurePostMessage)
    • 所需資源:HttpContext.Current.Server.HtmlEncode
    • 預估時間:0.1 小時
  2. 檢視所有插入點
    • 實作細節:對提示等字串同樣編碼(若非刻意輸出 HTML)。
    • 所需資源:程式碼審視
    • 預估時間:0.2 小時

關鍵程式碼/設定:

private static string HtmlEncode(string text)
{
    return HttpContext.Current.Server.HtmlEncode(text);
}

// 使用時
bodySB.AppendFormat("<b>{0}</b><p/>", HtmlEncode(SecurePostMessage));

實作環境:BlogEngine.NET 1.x、C#。 實測數據:

  • 改善前:訊息若含
  • 改善後:訊息以實體呈現,不被當作腳本。
  • 改善幅度:XSS 風險由高 → 低(可忽略)。

Learning Points(學習要點) 核心知識點:

  • XSS 與輸出編碼。
  • 安全輸出層的必要性。
  • 分辨可控 HTML 與非可控字串。

技能要求:

  • 必備技能:Web 安全基礎。
  • 進階技能:建立安全輸出工具層。

延伸思考:

  • 將輸出改為 Razor/控件以自動編碼?
  • 建立安全審查清單?
  • 對其他欄位(提示)同樣套用?

Practice Exercise(練習題)

  • 基礎:對所有動態字串加入 HtmlEncode。
  • 進階:寫一個安全輸出 helper 並重構引用。
  • 專案:安全性測試(嘗試插入 XSS)。

Assessment Criteria(評估標準)

  • 功能完整性(40%):不破壞現有功能。
  • 程式碼品質(30%):編碼覆蓋完整。
  • 效能(20%):編碼開銷可接受。
  • 創新(10%):安全工具層封裝。

Case #8: 利用 Post.Serving 事件攔截貼文輸出

Problem Statement(問題陳述)

業務場景:需在所有輸出貼文內容的地方(頁面、列表、細節)皆能觸發保護邏輯。 技術挑戰:確保擴充點能覆蓋所有貼文輸出場景,並在不同 ServingLocation 下表現正確。 影響範圍:若攔截不全,會出現可繞過的入口。 複雜度評級:中

Root Cause Analysis(根因分析)

直接原因:

  1. 不確定 BlogEngine 的輸出點是否統一。
  2. 缺少對 ServingLocation 的處理。
  3. 未驗證事件覆蓋範圍。

深層原因:

  • 架構層面:輸出事件是唯一穩定擴充點。
  • 技術層面:需理解事件參數與觸發時機。
  • 流程層面:POC 驗證重要。

Solution Design(解決方案設計)

解決策略:在 static constructor 掛 Post.Serving;於事件處理器中依據 e.Location 採取不同輸出策略,並以 POC 確認覆蓋率。

實施步驟:

  1. 事件掛載
    • 實作細節:Post.Serving += …
    • 所需資源:BlogEngine.Core
    • 預估時間:0.2 小時
  2. 覆蓋檢驗
    • 實作細節:在多處顯示貼文之頁面實測觸發。
    • 所需資源:手動測試
    • 預估時間:0.5 小時

關鍵程式碼/設定:

static SecurePost()
{
    Post.Serving += new EventHandler<ServingEventArgs>(Post_Serving);
}

private static void Post_Serving(object sender, ServingEventArgs e)
{
    // e.Location 可為 Feed 或一般頁面
    // 依場景決定輸出
}

實作環境:BlogEngine.NET 1.x、C#。 實測數據:

  • 改善前:未知是否能攔截所有輸出。
  • 改善後:POC 證實在各處輸出均觸發。
  • 改善幅度:覆蓋率由不確定 → 可驗證的 100%。

Learning Points(學習要點) 核心知識點:

  • BlogEngine 事件模型。
  • ServingLocation 差異。
  • 以 POC 驗證設計假設。

技能要求:

  • 必備技能:事件處理。
  • 進階技能:測試設計。

延伸思考:

  • 其他可攔截的事件?
  • 是否需要對摘要輸出也特殊處理?
  • 在多語主題/控制項下的表現?

Practice Exercise(練習題)

  • 基礎:在 Post.Serving 記錄觸發日誌。
  • 進階:對不同 Location 設置不同輸出。
  • 專案:撰寫覆蓋測試清單並回報結果。

Assessment Criteria(評估標準)

  • 功能完整性(40%):事件覆蓋到位。
  • 程式碼品質(30%):事件註冊與解除正確。
  • 效能(20%):事件處理開銷低。
  • 創新(10%):可視化偵錯工具。

Case #9: 單檔部署到 ~/App_Code/Extension 的低阻力安裝

Problem Statement(問題陳述)

業務場景:希望使用者可以透過複製單一 .cs 檔案即完成安裝,不需建置專案或操作資料庫。 技術挑戰:利用 ASP.NET 動態編譯特性,以及 BlogEngine Extension Manager 自動識別。 影響範圍:安裝/升級成本、擴散與採用率。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 使用者不熟悉建置與部署流程。
  2. 多檔案/多步驟降低採用率。
  3. 需要快速試用與回饋。

深層原因:

  • 架構層面:App_Code 支援動態編譯。
  • 技術層面:Extension Manager 自動列示。
  • 流程層面:減少部署阻力提升成功率。

Solution Design(解決方案設計)

解決策略:將完整 SecurePost.cs 投放到 ~/App_Code/Extension 資料夾;登入管理後台,即可在 Extension Manager 看到並進行設定。

實施步驟:

  1. 檔案投放
    • 實作細節:放入正確目錄,無須重建。
    • 所需資源:檔案系統權限
    • 預估時間:0.1 小時
  2. 後台啟用與設定
    • 實作細節:在 Extension Manager 編輯三項設定。
    • 所需資源:管理者登入
    • 預估時間:0.2 小時

關鍵程式碼/設定:

部署路徑:~/App_Code/Extension/SecurePost.cs
後台:Settings → Extensions → SecurePost(Edit)

實作環境:BlogEngine.NET 1.x、IIS。 實測數據:

  • 改善前:需複雜建置/部署步驟。
  • 改善後:單檔部署即可。
  • 改善幅度:部署步驟數大幅降低(可由數步縮至 1 步)。

Learning Points(學習要點) 核心知識點:

  • App_Code 動態編譯。
  • Extension Manager 功能。
  • 低摩擦部署策略。

技能要求:

  • 必備技能:基本檔案操作、IIS。
  • 進階技能:部署自動化腳本。

延伸思考:

  • 是否提供 zip 套件與檢查清單?
  • 加入版本檢查與相容性訊息?
  • 提供反安裝(移除)指引?

Practice Exercise(練習題)

  • 基礎:手動部署並啟用。
  • 進階:PowerShell 腳本自動部署。
  • 專案:製作 CI/CD 任務自動發佈 Extension。

Assessment Criteria(評估標準)

  • 功能完整性(40%):部署後可運作。
  • 程式碼品質(30%):無硬編路徑與環境依賴。
  • 效能(20%):部署不影響站台啟動。
  • 創新(10%):自動化與回滾機制。

Case #10: Feed 場景避免輸出互動式 UI

Problem Statement(問題陳述)

業務場景:RSS/Atom 訂閱器無法互動輸入密碼;若輸出表單按鈕將破壞閱讀體驗或格式。 技術挑戰:需辨識 ServingLocation 為 Feed 時,改採簡單訊息而非表單。 影響範圍:避免 feed 壞版或無法閱讀。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 訂閱器不支援互動 UI。
  2. 互動元素會影響 feed 解析與呈現。
  3. 未區分場景將導致異常。

深層原因:

  • 架構層面:不同輸出管道需求不同。
  • 技術層面:需檢查 e.Location。
  • 流程層面:場景分歧時的差異化輸出。

Solution Design(解決方案設計)

解決策略:在 Post_Serving 中判斷 e.Location;若為 Feed,僅輸出純文字訊息;否則輸出提示+輸入框。

實施步驟:

  1. 場景判斷
    • 實作細節:if (e.Location == ServingLocation.Feed) { … }
    • 所需資源:ServingEventArgs
    • 預估時間:0.1 小時
  2. 分支輸出
    • 實作細節:Feed 僅保留訊息;頁面輸出表單。
    • 所需資源:StringBuilder
    • 預估時間:0.1 小時

關鍵程式碼/設定:

if (e.Location == ServingLocation.Feed)
{
    // 僅輸出提示,不放互動元素
}
else
{
    // 輸出輸入框+按鈕
}

實作環境:BlogEngine.NET 1.x、C#。 實測數據:

  • 改善前:Feed 可能出現不相容的互動元素。
  • 改善後:Feed 只顯示保護訊息。
  • 改善幅度:Feed 相容性由不確定 → 穩定可讀。

Learning Points(學習要點) 核心知識點:

  • 場景感知式輸出。
  • Feed 內容限制。
  • 一碼多用的風險。

技能要求:

  • 必備技能:C# 條件分支。
  • 進階技能:Feed 測試與驗證。

延伸思考:

  • 是否提供 feed 上的替代連結至解鎖頁?
  • 是否以摘要替代完整內容?
  • 是否標註加密狀態讓客戶端可識別?

Practice Exercise(練習題)

  • 基礎:為 Feed 輸出不同內容。
  • 進階:Feed 顯示摘要與跳轉連結。
  • 專案:Feed 客端插件,可提示使用者前往解鎖。

Assessment Criteria(評估標準)

  • 功能完整性(40%):Feed 不破版。
  • 程式碼品質(30%):分支清晰。
  • 效能(20%):分支判斷成本低。
  • 創新(10%):友善的替代體驗。

Case #11: 明碼 QueryString 與雜湊的安全取捨

Problem Statement(問題陳述)

業務場景:作者明確選擇不做雜湊(MD5/SHA256)而採明碼 URL,因為要最小化開發與部署成本。 技術挑戰:如何評估風險並制訂底線,使保護不至於形同虛設? 影響範圍:密碼可能出現在歷史紀錄、Referer 或被側錄;需有風險說明。 複雜度評級:中

Root Cause Analysis(根因分析)

直接原因:

  1. 客端與伺服端同時支持雜湊需引入額外腳本/程式。
  2. 時間成本與複雜度不符合「快速」目標。
  3. 保護需求為「基本遮蔽」而非高安全。

深層原因:

  • 架構層面:無現成前端加密基礎。
  • 技術層面:GET 明碼的固有限制。
  • 流程層面:需求優先級與風險容忍度的平衡。

Solution Design(解決方案設計)

解決策略:明確遵循兩原則(server-side 比對+未通過不輸出內容),接受明碼 QueryString 帶來的有限風險,並在說明中提示使用者安全界線;後續可選擇提升(POST + Session 或雜湊)。

實施步驟:

  1. 實作最小可行保護
    • 實作細節:維持現有 GET 與 server-side 比對。
    • 所需資源:現有程式碼
    • 預估時間:0 小時
  2. 文件化風險與升級路線
    • 實作細節:在說明或設定 Help 中加入安全提醒。
    • 所需資源:文件/Help
    • 預估時間:0.2 小時

關鍵程式碼/設定:

安全底線:
- 密碼僅在伺服器端比對
- 未通過不輸出原文
- URL 中可能出現密碼(使用者需知悉)

實作環境:BlogEngine.NET 1.x。 實測數據:

  • 改善前:功能未實現。
  • 改善後:在可接受風險前提下快速可用。
  • 改善幅度:交付時間顯著縮短(同日完成)。

Learning Points(學習要點) 核心知識點:

  • 安全/成本/時程的三角權衡。
  • 風險溝通與文件化。
  • 升級策略設計。

技能要求:

  • 必備技能:威脅模型與風險評估。
  • 進階技能:分階段安全升級設計。

延伸思考:

  • 雜湊與鹽:避免重放攻擊?
  • 以一次性 token 或暫存 cookie 降低重複傳輸密碼?
  • 加上 HTTPS 避免竊聽?

Practice Exercise(練習題)

  • 基礎:文件化安全注意事項。
  • 進階:改以 POST + Session 保存通過狀態。
  • 專案:加入 SHA-256 client/server 雜湊驗證。

Assessment Criteria(評估標準)

  • 功能完整性(40%):仍能滿足核心需求。
  • 程式碼品質(30%):變更範圍小、清晰。
  • 效能(20%):不引入多餘負擔。
  • 創新(10%):提供升級路徑。

Case #12: 避開 IIS 整合式驗證造成的誤封與成本

Problem Statement(問題陳述)

業務場景:先前嘗試以 IIS 整合式驗證保護內容,但結果是「該看的人看不到、該擋的沒擋住」,最終放棄。 技術挑戰:IIS 層級設定過於粗糙,不利於單篇控制與靈活策略。 影響範圍:使用者體驗負面、維護困難、誤封風險高。 複雜度評級:中

Root Cause Analysis(根因分析)

直接原因:

  1. IIS 無法輕鬆做到 per-post 控制。
  2. 站台/虛擬目錄層級授權難以細緻化。
  3. 管理與除錯門檻高。

深層原因:

  • 架構層面:IIS 更適合站台層控管。
  • 技術層面:需應用層(BlogEngine)內的上下文資訊。
  • 流程層面:營運流程不宜依賴高摩擦設定。

Solution Design(解決方案設計)

解決策略:將存取控制下放至應用層,以 BlogEngine Extension 在輸出點細緻化控制;不再採 IIS 角色/整合式驗證。

實施步驟:

  1. 移除 IIS 規則
    • 實作細節:關閉整合式驗證對內容頁。
    • 所需資源:IIS 管理權限
    • 預估時間:0.2 小時
  2. 啟用 Extension 控制
    • 實作細節:完成攔截與比對(本擴充)。
    • 所需資源:BlogEngine 管理
    • 預估時間:0.5 小時

關鍵程式碼/設定:

IIS:取消對內容 URL 的整合式驗證規則
App:由 Extension 在 Post.Serving 判斷與保護

實作環境:IIS 6/7、BlogEngine.NET。 實測數據:

  • 改善前:誤封與體驗差。
  • 改善後:單篇控制精準、體驗一致。
  • 改善幅度:授權誤差由高 → 低(接近 0)。

Learning Points(學習要點) 核心知識點:

  • 應用層 vs 伺服器層授權邊界。
  • 就地取用應用層上下文(貼文、使用者)。
  • 最小驚擾原則。

技能要求:

  • 必備技能:IIS/應用部署。
  • 進階技能:授權架構設計。

延伸思考:

  • 是否保留 IaaS 層整體保護+應用層細化?
  • 對靜態資源(圖片)如何處理?
  • 記錄與稽核如何落地?

Practice Exercise(練習題)

  • 基礎:關閉不必要的 IIS 規則。
  • 進階:設計應用層授權流程圖。
  • 專案:導入日誌紀錄與告警。

Assessment Criteria(評估標準)

  • 功能完整性(40%):應用層授權完整。
  • 程式碼品質(30%):責任邊界清晰。
  • 效能(20%):不過度依賴 IIS 過濾。
  • 創新(10%):混合策略設計。

Case #13: 以 Early Return 與 StringBuilder 最小化效能負擔

Problem Statement(問題陳述)

業務場景:Post.Serving 對每篇輸出都會觸發,需確保對未保護的貼文不產生顯著負擔。 技術挑戰:在事件中執行最少的工作;避免不必要的字串拼接與分支。 影響範圍:全站效能與回應時間。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 所有輸出都會進入事件處理。
  2. 多餘運算會累積成明顯負擔。
  3. 無效字串操作成本高。

深層原因:

  • 架構層面:事件廣泛觸發。
  • 技術層面:早退模式可減少成本。
  • 流程層面:以快路徑優先設計。

Solution Design(解決方案設計)

解決策略:以條件早退排列:已登入 → 密碼正確 → 非保護文章 → 才進入輸出替換;僅在必要時建立 StringBuilder 與組合 HTML。

實施步驟:

  1. 條件排序
    • 實作細節:將命中率高的放前面(已登入、密碼正確)。
    • 所需資源:現有程式
    • 預估時間:0.1 小時
  2. 延遲初始化
    • 實作細節:需要時才 new StringBuilder。
    • 所需資源:現有程式
    • 預估時間:0.1 小時

關鍵程式碼/設定:

if (HttpContext.Current.User.Identity.IsAuthenticated) return;
if (HttpContext.Current.Request["pwd"] == Password) return;
if (!e.Body.StartsWith("[password]", StringComparison.CurrentCultureIgnoreCase)) return;

// 僅此時才建立字串
var bodySB = new StringBuilder();

實作環境:BlogEngine.NET 1.x。 實測數據:

  • 改善前:可能對所有貼文做不必要處理。
  • 改善後:未保護貼文幾乎零開銷。
  • 改善幅度:平均處理成本顯著下降(快路徑占多數)。

Learning Points(學習要點) 核心知識點:

  • 快路徑/慢路徑設計。
  • 字串處理成本意識。
  • 事件處理器效能最佳化。

技能要求:

  • 必備技能:C# 效能基本功。
  • 進階技能:剖析與監控。

延伸思考:

  • 加入快取(如通過狀態)是否更佳?
  • 指標收集(命中率、延遲)如何做?
  • 字串池與 GC 影響?

Practice Exercise(練習題)

  • 基礎:重排條件邏輯以提高快路徑命中。
  • 進階:引入簡單快取減少重複比對。
  • 專案:建立計量(Performance counters/log)。

Assessment Criteria(評估標準)

  • 功能完整性(40%):不影響功能行為。
  • 程式碼品質(30%):邏輯順序清楚。
  • 效能(20%):快路徑明顯。
  • 創新(10%):監控與量測。

Case #14: 禁止以 DHTML 方式「藏起來」,改為服務端內容替換

Problem Statement(問題陳述)

業務場景:常見錯誤做法是用 CSS/JavaScript 將內容隱藏,造成內容仍可於原始碼中被讀取。 技術挑戰:需改為服務端決策,不把原文送到客戶端未授權者手上。 影響範圍:DHTML 藏文形同虛設,易造成數據外洩。 複雜度評級:低

Root Cause Analysis(根因分析)

直接原因:

  1. 前端「藏」不等於不傳送。
  2. 攻擊者可直接查看 HTML 原始碼。
  3. 缺乏正確的授權關卡。

深層原因:

  • 架構層面:缺少內容輸出控制點。
  • 技術層面:忽略 server-side gating。
  • 流程層面:安全設計吃虧於便捷性偏好。

Solution Design(解決方案設計)

解決策略:在 Post.Serving 中完全替換 e.Body 為提示內容,未驗證前不渲染原文;通過後才保留原有 e.Body。

實施步驟:

  1. 嚴格覆寫未通過輸出
    • 實作細節:e.Body = bodySB.ToString()。
    • 所需資源:現有程式
    • 預估時間:0.1 小時
  2. 通過驗證保留原文
    • 實作細節:return 保持原樣。
    • 所需資源:現有程式
    • 預估時間:0.1 小時

關鍵程式碼/設定:

// 未通過:覆寫
e.Body = bodySB.ToString();

// 通過:return,不動原文

實作環境:BlogEngine.NET 1.x。 實測數據:

  • 改善前:內容於原始碼仍可見。
  • 改善後:未通過時原文不會被送出。
  • 改善幅度:資料外洩風險顯著下降。

Learning Points(學習要點) 核心知識點:

  • 輸出替換 vs 前端隱藏。
  • 服務端授權才有效。
  • 內容保護最小原則。

技能要求:

  • 必備技能:C# 基本流控制。
  • 進階技能:安全思維。

延伸思考:

  • 下載附件/圖片如何保護?
  • 以反向代理/簽名 URL 保護靜態資源?
  • 加上稽核日誌?

Practice Exercise(練習題)

  • 基礎:用 server-side 條件控制輸出。
  • 進階:對附件連結輸出簽名 URL。
  • 專案:建立統一的內容保護中介層。

Assessment Criteria(評估標準)

  • 功能完整性(40%):未通過不能見原文。
  • 程式碼品質(30%):覆寫與放行清晰。
  • 效能(20%):不多做工。
  • 創新(10%):資源型內容保護延伸。

Case #15: 全站共用單一密碼的管理便利與風險

Problem Statement(問題陳述)

業務場景:為求簡單,所有受保護貼文共用同一組密碼,由設定頁統一管理;適合小型私密分享情境。 技術挑戰:在便利與安全性之間取捨;平衡操作簡易性與被擴散的風險。 影響範圍:密碼外洩將影響所有受保護貼文;但管理成本最低。 複雜度評級:中

Root Cause Analysis(根因分析)

直接原因:

  1. 使用者不願管理多組密碼。
  2. Extension 設定預設僅一組值(IsScalar = true)。
  3. 需求屬「輕量保護」。

深層原因:

  • 架構層面:單一設定最簡潔。
  • 技術層面:多密碼需複雜 UI 與儲存。
  • 流程層面:管理成本優先於安全深度。

Solution Design(解決方案設計)

解決策略:維持單一 PasswordValue 設定;在說明中揭露風險;必要時提供後續升級選項(改 IsScalar=false 以支援多組密碼或 per-post metadata)。

實施步驟:

  1. 單一密碼設定
    • 實作細節:_settings.GetSingleValue(“PasswordValue”)
    • 所需資源:ExtensionSettings
    • 預估時間:0.1 小時
  2. 風險揭露與升級路線
    • 實作細節:設定 Help 或文件註明。
    • 所需資源:文件
    • 預估時間:0.1 小時

關鍵程式碼/設定:

private static string Password
{
    get { return _settings.GetSingleValue("PasswordValue"); }
}

實作環境:BlogEngine.NET 1.x。 實測數據:

  • 改善前:無法快速統一管理保護密碼。
  • 改善後:後台一處修改即全站生效。
  • 改善幅度:管理步驟由多處 → 單一處(-80% 以上)。

Learning Points(學習要點) 核心知識點:

  • 單密碼策略的適用情境。
  • IsScalar 單組設定的設計含義。
  • 升級到多密碼的路線。

技能要求:

  • 必備技能:設定存取。
  • 進階技能:設定模型設計。

延伸思考:

  • per-post 密碼是否以自訂欄位實作更佳?
  • 是否支援以分類(Category)分組密碼?
  • 加上密碼輪替政策?

Practice Exercise(練習題)

  • 基礎:讀寫 PasswordValue 並即時生效。
  • 進階:將 IsScalar 改為 false 支援多密碼。
  • 專案:設計 per-post 密碼 UI 與儲存。

Assessment Criteria(評估標準)

  • 功能完整性(40%):單一密碼生效。
  • 程式碼品質(30%):設定讀寫健壯。
  • 效能(20%):變更後不需重開站。
  • 創新(10%):多密碼升級設計。

============================== 案例分類 ==============================

  1. 按難度分類
    • 入門級(適合初學者):
      • Case 2(Server-Side 驗證)、Case 3([password] 開關)、Case 4(登入放行)
      • Case 5(QueryString 互動)、Case 7(HtmlEncode 安全)
      • Case 9(單檔部署)、Case 10(Feed 分支)、Case 13(Early Return)
    • 中級(需要一定基礎):
      • Case 1(完整單篇保護)、Case 6(Extension Manager 設定)
      • Case 8(Post.Serving 事件覆蓋)、Case 12(IIS vs 應用層)
      • Case 15(單密碼策略)
    • 高級(需要深厚經驗):
      • Case 11(安全取捨與升級路線)
  2. 按技術領域分類
    • 架構設計類:
      • Case 1、Case 8、Case 12、Case 15
    • 效能優化類:
      • Case 13
    • 整合開發類:
      • Case 5、Case 6、Case 9、Case 10
    • 除錯診斷類:
      • Case 8(POC 與覆蓋驗證)
    • 安全防護類:
      • Case 2、Case 4、Case 7、Case 11、Case 14
  3. 按學習目標分類
    • 概念理解型:
      • Case 2、Case 11、Case 12、Case 14、Case 15
    • 技能練習型:
      • Case 3、Case 5、Case 6、Case 7、Case 9、Case 10、Case 13
    • 問題解決型:
      • Case 1、Case 4、Case 8
    • 創新應用型:
      • Case 6(擴展設定模型)、Case 11(安全升級路徑)、Case 15(策略演進)

============================== 案例關聯圖(學習路徑建議) ==============================

  • 建議先學: 1) Case 8(理解 Post.Serving 事件與覆蓋範圍) 2) Case 3(以 [password] 作為啟用開關) 3) Case 2(Server-Side 驗證原則)與 Case 14(為何不能用 DHTML 藏)

  • 進一步練習: 4) Case 4(登入放行)與 Case 5(QueryString 互動) 5) Case 7(HtmlEncode 安全)與 Case 10(Feed 場景處理) 6) Case 6(Extension Manager 設定)與 Case 9(單檔部署)

  • 進階與策略: 7) Case 13(Early Return 效能最佳化) 8) Case 12(IIS vs 應用層授權邊界) 9) Case 11(明碼 vs 雜湊安全取捨與升級路徑) 10) Case 15(單密碼策略與未來多密碼/每篇密碼演進)

  • 依賴關係:
    • Case 1(完整方案)建立在 Case 2/3/4/5/6/7/8 的概念與技術之上。
    • Case 10 依賴 Case 8 對 ServingLocation 的理解。
    • Case 11 與 Case 15 為策略與安全層的總結與演進,依賴前面功能已完成。
  • 完整學習路徑建議: Case 8 → Case 3 → Case 2 → Case 14 → Case 4 → Case 5 → Case 7 → Case 10 → Case 6 → Case 9 → Case 13 → Case 12 → Case 11 → Case 15 → 最後整合為 Case 1 的完整實作與交付。





Facebook Pages

AI Synthesis Contents

- 原始文章內容
- 問答集
- 文章摘要
- 解決方案 / Case Study

Edit Post (Pull Request)

Post Directory