共用方式為


Microsoft.com 幕後ASP.NET 的管理與委派

Jeff Toews

microsoft.com 的 Web 基礎結構幾乎完全是以 .NET Framework 2.0 執行。microsoft.com 營運小組重要的 Web 管理挑戰之一,就是正確設定 ASP.NET,而我們也已在嘗試將我們的設定調整至盡善盡美的過程中,獲得許多經驗。設定

正確的組態時,需要對於 web.config 和 machine.config 檔案中的各種組態區段具有充分的操作知識,並且瞭解這些設定所代表的意義。若想很快地掌控設定及其含義,參考範例是一個好方法。在本專欄中,我將說明在設定執行 microsoft.com 網站的伺服器時所得到的一些組態密訣,這樣一來,您可以從我們的經驗中學習,過程會輕鬆不少。

1. 正確地設定編譯參數

在生產環境中部署 ASP.NET 應用程式時,一定要確定不會有人在任何應用程式的 web.config 檔案中,不小心 (或故意) 地將編譯偵錯屬性的設定保留為 True,如以下所示:

<compilation debug="true" />

在需要管理許多 Web 應用程式的忙碌環境中,您會想要使用 ASP.NET 組態控制機制來防止這種狀況發生。(稍後我會針對此點提供更多詳細資料)。

另外,請務必不要在個別的 .aspx 中將頁面特定的偵錯屬性設定為 True (如下所示),這點也很重要:

<%@ Page debug="true" %>

但是,在發行量很高的忙碌環境中,期待所有的 .aspx 頁面在發行之前都一定要移除這項設定,往往是不切實際的。您需要整體的方法來防止這種情況發生。

使用這項設定來編譯 Web 應用程式,會導致二進位檔處於偵錯狀態,而非零售狀態。另外,您的程式碼也不會最佳化,因而效能低落。此外,您的 ASP.NET 要求也不會逾時,因為偵錯設定會防止逾時。在生產環境中使用偵錯版本,就像是敞開大門邀請駭客上門一樣!

幸運的是,Microsoft® .NET Framework 2.0 具有新的 machine.config 部署設定,會告訴 ASP.NET 停用下列項目:偵錯功能、追蹤輸出和 ASP.NET 錯誤訊息的顯示 (本機和遠端),不論 web.config 檔案中的指示或特定的頁面屬性為何。程式碼看起來像這樣:

<configuration>
    <system.web>
          <deployment retail="true"/>
    </system.web>
</configuration>

請注意,後面兩個優點 (停用追蹤輸出和停用遠端顯示詳細的 ASP.NET 錯誤訊息) 是您真的應該採用的安全性最佳作法。如果不這麼做,就好像將應用程式的內部工作暴露於全世界之前,任人宰割一樣。

趁我在談這個主題時,再告訴您另一個重要的事實:在 <location allowOverride="false"> 內部的根 web.config 檔案中鎖定 <system.web><compilation> 偵錯屬性,或使用 lockItems 屬性,可避免應用程式組態階層中位於較低階層的 web.config 檔案開啟偵錯設定,但這並不能避免個別的 .aspx 頁面在其頁面屬性中啟用偵錯模式。部署零售參數是唯一可以完全停用所有階層之 ASP.NET 偵錯功能的方式。

將部署零售參數設定為 True,可能是任何具有正式生產伺服器的公司,在確保應用程式永遠能在可能情況下以最佳效能執行,並且沒有任何安全性資訊漏洞時,所應遵循的最佳作法。如我先前談到的,這是 ASP.NET 2.0 中的全新參數,也是 ASP.NET 團隊從使用者的意見直接反映出來的結果。

相反地,如果是開發人員需要對其 Web 應用程式進行偵錯的對內預先生產環境,則不要使用部署零售設定。只需在預先生產的根 web.config 檔案中設定 <compilation debug="false">,並讓這個值由個別應用程式的 web.config 檔案或 .aspx page 屬性覆寫即可。

2. 在 ASP.NET 2.0 中使用中信任等級

如果,如同 microsoft.com 上的許多網站一樣,您在將網站或應用程式移轉為 ASP.NET 2.0 之後,仍持續使用「完全」或「高」信任等級,請再次考量現在的「中」信任等級所能達到的效果。例如,受限的 WebPermission 會將應用程式的通訊僅限於您在 <trust> 元素中所定義的位址或位址範圍。如此就可以控制及維護認可的外部網站清單以及可以從遠端呼叫的位址範圍。這對安全性來說可是一大建樹。

FileIOPermission 也受到限制。這代表應用程式的程式碼只能存取其虛擬目錄階層中的檔案。依預設,在「中」信任等級下,只會針對每個應用程式的虛擬目錄階層授與「讀取」、「寫入」、「附加」和「路徑探索」等權限。這麼一來就不會再進行隨機的檔案 I/O 存取 – 在裝載許多應用程式的共用 Web 環境中,這是相當重要的。

另一個優點是會移除未受管理的程式碼權限。這表示您可以避免使用舊版的元件,到目前為止,這是我們所找到的停用 Aspcompat 頁面屬性最簡易的方式。(將 Aspcompat 設定為 True 可能導致頁面效能降低)。

ASP.NET 2.0 的「中」信任等級可以為管理員提供針對前述的每個預設限制建立自訂例外狀況的能力,就這點而言,該項設定就相當有彈性。在 ASP.NET 1.1 中就缺乏這種彈性。在 ASP.NET 2.0 中以「中」信任等級執行比在 ASP.NET 1.1 中簡單的另一個原因是,您可以存取 Microsoft SQL Server™ 資料庫。

因此,如果要在同一台伺服器上裝載多個應用程式,可以使用程式碼存取安全性以及「中」信任等級來提供應用程式隔離。藉由在根 web.config 檔案中設定並鎖定信任等級 (使用 <location allowOverride="false"> 標記),您可以針對伺服器上的所有 Web 應用程式建立安全性原則。藉由設定 allowOverride="false" (如 [圖 1] 所示),個別的開發人員將無法在其應用程式的 web.config 檔案中覆寫「中」信任原則設定。

Figure 1 信任設定

<configuration>
    <location allowOverride="false">
        <system.web>
            <securityPolicy>
                <trustLevel name="Full" policyFile="internal" />
                <trustLevel name="High" policyFile="web_hightrust.config" />
                <trustLevel name="Medium" policyFile="web_mediumtrust.config" />
                <trustLevel name="Low"  policyFile="web_lowtrust.config" />
                <trustLevel name="Minimal" policyFile="web_minimaltrust.config" />
            </securityPolicy>
            <trust level="Medium" originUrl="" />
        </system.web>
    </location>
</configuration>

您可於下列文章中找到有關在 ASP.NET 2.0 中使用「中」信任等級的詳細資訊:<How To:Use Medium Trust in ASP.NET 2.0>(英文)。

3. 限制指定檔案類型的下載

在伺服器上有些檔案類型是您絕對不想要落入壞人手中的。幸運的是,ASP.NET 依預設會設定為截取和停止用於 ASP.NET 應用程式中的數種不同檔案類型的要求。這些檔案包括 .config 檔案和儲存應用程式原始碼的 .cs 檔案。ASP.NET 可將兩種檔案類型與 System.Web.HttpForbiddenHandler 建立關聯,以確保檔案的隱私。這個處理常式在啟動後,會對要求檔案的使用者傳回錯誤。事實上,您可以使用這個方法來限制任何檔案類型。

例如,在 microsoft.com 網站上就不允許 .asmx 檔案類型,方法是將下列項目加入至根 web.config 檔案的 <system.web><httpHandlers> 區段。

<add path="*.asmx" verb="*" type=
    "System.Web.HttpForbiddenHandler" />

如您所見,您可以在 <httpHandlers> 元素中使用 <add> 子標記來指定要封鎖的其他檔案類型。請將動詞屬性設定為等於 "*"。當您這麼做時,就等於是針對該檔案類型封鎖所有的 HTTP 要求類型。將路徑屬性定義為萬用字元,以符合要封鎖的檔案類型。例如,您可以指定 "*.mdb"。最後,將類型屬性設定為 "System.Web.HttpForbiddenHandler"。

4. 在新增組件參照時請小心

每個執行 .NET Framework 的 Web 伺服器都有適用於整個電腦的程式碼快取,稱為 Global Assembly Cache (GAC)。GAC 會儲存特別指定為由電腦上多個應用程式共用的組件。如果有多個不同的應用程式需要參照這些組件 (即使 Web 伺服器上大多數的網站並不會存取這些組件),則將它們加入 GAC 是合理的。這並不會導致效能/資源明顯降低,還可提供維護集中式版本控制的優點,而不是讓共用的組件四散於伺服器中個別應用程式的 /bin 資料夾內。

不過,何時應該將組件參照加入至 root web.config 的條件,則需要比決定何時將組件置於 GAC 中的條件嚴格許多。讓個別的應用程式針對並非完全全域的元件,在其應用程式的 web.config 檔案中加入組件參照,比在根 web.config 檔案中宣告這些組件參照,在效能上會高出許多。這樣可以為 Web 伺服器上所有不使用這些組件的應用程式大幅節省頁面載入的時間,因為編譯器不需要花時間載入不必要的組件。ASP.NET 編譯器不會只因為組件位於 GAC 中,就為應用程式載入該組件;只有在其 appdomain 或較高的應用程式領域中有組件的參照存在時,此編譯器才會載入該組件。結果是,應用程式開發人員可以在 microsoft.com 中,在其應用程式的 web.config 檔案中覆寫所有 Microsoft.com 環境的 <configuration><system.web><compilation><assemblies> 元素。

特別是,在 microsoft.com 生產和執行環境的根 web.config 檔案中,<system.web><compilation> 節點的所有屬性 (包括 debug、explicit、defaultLanguage) 及所有元素 (buildProviders、expressionBuilders 等等;<assemblies> 元素除外) 都會鎖定:

<compilation debug="false" 
    explicit="true" defaultLanguage="vb" 
    numRecompilesBeforeAppRestart="500" 
    lockAttributes="*" lockAllElementsExcept=
        "assemblies" >

在對內的預先生產環境中,根 web.config 檔案的 <system.web><compilation> 區段會鎖定,但我們允許應用程式發行者特別覆寫 <assemblies> 元素和 debug="false" (以啟用疑難排解/偵錯):

<compilation debug="false" explicit="true"
    defaultLanguage="vb" 
    numRecompilesBeforeAppRestart="500" 
    lockAllAttributesExcept="debug" 
    lockAllElementsExcept="assemblies" >

請注意,此處使用的鎖定屬性是 ASP.NET 2.0 的新增功能。同樣地,如需這些屬性的詳細資訊及其使用範例,請參閱 General Attributes Inherited by Section Elements (英文)。

5. 移除手動設定的 MaxConnection 值

幾乎所有的 microsoft.com 網站都具有 ASP.NET 應用程式,可以呼叫遠端的 Web 服務叢集。可從單一的 Web 伺服器同時進行的遠端 Web 服務呼叫最大數目,是由 machine.config 檔案中 <connectionManagement> 元素的 maxConnection 屬性決定。在 ASP.NET 1.1 中,maxConnection 值預設是設定為 2。這個舊的預設 maxConnection 值對於 microsoft.com 這種擁有數百個不同應用程式會進行遠端 Web 服務呼叫的類似網站而言,實在過低。其結果是,在等候遠端 Web 服務呼叫完成時,ASP.NET 要求會排入佇列。(您可透過 perfmon counter ASP.NET\Requests Queued 來檢視佇列的 ASP.NET 要求數目)。為了讓更多的遠端 Web 服務呼叫可以同時執行 (並藉此改善網站上應用程式的效能),我們將使用四個處理器的 Web 伺服器的 maxConnection 值增加到 40 (一般建議的 maxConnection 值為 CPU 數目的 12 倍,但是請依照您的特定狀況來調整此值)。

不過,在 ASP.NET 2.0 中已經不再需要手動設定 maxConnection 了,因為此值現在會自動調整及設定。這是 machine.config 中 processModel 標記的新組態區段所造成的結果 (如需 processModel 元素的詳細資訊,請參閱 processModel Element (ASP.NET Settings Schema) (英文))。

<system.web>
    <processModel autoConfig="true" />
</system.web>  

在 machine.config 中啟用 autoConfig 時 (此為預設設定),ASP.NET 會將 maxConnection 參數的值設定為 12n (其中 n 為 CPU 的數目)。啟用 autoConfig 也會導致下列狀況:maxWorkerThreads 參數和 maxIoThreads 參數會設定為 100,minFreeThreads 參數設定為 88n,minLocalRequestFreeThreads 參數設定為 76n,而 minWorkerThreads 則設定為 50。

在 ASP.NET 2.0 中使用 autoConfig 以自動調整並設定 maxConnection 和清單中其他屬性值之前,請務必移除這些參數的手動設定值,因為這些值可能會取代 autoConfig 值而被使用。從 ASP.NET 1.1 (需要明確地設定 maxConnection) 遷移至 ASP.NET 2.0 (具有預設值) 時,需要記住這點。

同樣地,maxConnection 的 autoConfig 值和先前所列出的其他屬性,在某種程度上都是隨意自定的,而且可能無法用於每個執行個體,但我發現這些限制幾乎對所有的 microsoft.com 應用程式都非常適用。

如果決定需要手動調整 maxConnection 值,在增加其值時請小心,因為這可能會導致 CPU 使用量增加。使用量增加的原因是由於 ASP.NET 現在可以處理較多的連入要求,而不必讓它們依序等候呼叫 Web 服務。當然,您應該記住,maxConnection 屬性並不會影響本機的 Web 服務呼叫,而只會對遠端呼叫造成影響。

6. 注意未處理的例外狀況

將 ASP.NET 1.1 網站或應用程式轉換為 ASP.NET 2.0 版本時,注意在未處理例外狀況的預設原則中發生了什麼重大變更將極為有用。在 .NET Framework 1.1 和 1.0 中,在 Managed 執行緒上未處理的例外狀況會遭到忽略,而且因為應用程式會持續執行,所以這些例外狀況常會保持隱匿。除非附加捕捉例外狀況的偵錯程式,否則您不會知道發生了任何問題。不過在 ASP.NET 2.0 下,在擲出未處理的例外狀況時,ASP.NET 應用程式會意外結束。如果舊的預設例外狀況處理原則先前遮掩了許多未處理的例外狀況,這就可能對網站或應用程式的可用性造成嚴重的衝擊。

解決這個問題的最佳方式,就是進行徹底的測試,並排除未處理的例外狀況 (其實應用程式中真的不該出現例外狀況)。不過,如果要遷移非常大型的應用程式 (很難判斷出現例外狀況的位置),或者如果需要遷移許多舊版應用程式 (很難進行徹底的個別測試),則您有幾個選擇。將 microsoft.com 網站轉換為 ASP.NET 2.0 版本時,我們將未處理的例外狀況原則變更回 ASPNET 1.1 和 ASP.NET 1.0 中的預設行為。

若要啟用這個舊版的預設例外狀況處理行為,請將下列程式碼加入至 aspnet.config 檔案:

<configuration>
    <runtime>
        <legacyUnhandledExceptionPolicy 
            enabled="true" />
    </runtime>
</configuration>

此程式碼位於下列兩個資料夾中:

%WINDIR%\Microsoft.NET\Framework\v2.0.50727 (位於 x86 或 SYSWOW64 系統) 和 %WINDIR%\Microsoft.NET\Framework64\v2.0.50727 (位於 x64 系統)。

這項變更基本上會將 .NET Framework 還原為 1.1 和 1.0 的舊版行為。請將這種方法視為短期的修正方式,因為最終真正會造成困擾的,還是應用程式的遮罩問題。不過,在避免由於意外終止之工作者處理序所造成的可用性問題時,這還是非常方便的方法。如需此項行為變更的詳細資訊,請參閱未處理的例外狀況會導致 ASP.NET 應用程式在 .NET Framework 2.0 中意外結束

7. 確定 Proxy 伺服器組態正確

Web 伺服器管理員可以藉由設定 machine.config 檔案中的 <configuration><system.net><defaultProxy> 元素,將 Proxy 伺服器指定為用於網際網路的 HTTP 要求。

在 microsoft.com 的生產環境中,我們將 <defaultProxy> 值設定為使用系統的預設 proxy (因為沒有安裝防火牆用戶端),並且使用 <location allowOverride="false"> 標記來防止 <defaultProxy> 元素遭到應用程式開發人員的覆寫 (可能會因一時不察而使用內部 Proxy 伺服器發行 web.config 檔案):

<configuration>
    <location allowOverride="false">
       <system.net>
          <defaultProxy>
              <proxy usesystemdefault="true" />
          </defaultProxy>
       </system.net>
    </location>
</configuration>

在 microsoft.com 內部的預先生產和執行環境中,我們將 usesystemdefault 屬性設定為 False,將 bypassonlocal 設定為 True,並且加入 proxy bypasslist (請參閱 [圖 2])。bypasslist 會列出一般的表示式,描述不使用指定 Proxy 的位址。同樣地,此區段是包含在 <location allowOverride="false"> 標記內,可防止開發人員在其 web.config 檔案中自行指定 proxy 伺服器。(這些通常是內部的 proxy 伺服器,而且在頁面發行至生產環境後,就無法再對其進行呼叫)。任何在預先生產或執行階段中指定 proxy 伺服器的嘗試,都會導致執行階段的 ASP.NET 錯誤,因此開發人員在發行至生產環境之前,必須先移除這項組態。

Figure 2 設定 Bypasslist

<configuration>
    <location allowOverride="false">
        <system.net>
             <defaultProxy>
                <proxy
usesystemdefault="false"
proxyaddress = "http://proxy.server.foo.com:80"
bypassonlocal = "true" />

                <bypasslist>
<add address="10\.*"/>
<add address="dns\.foo\.com" />
<add address="name1\.name2\.foo\.com" />
                </bypasslist>
            </defaultProxy>
        </system.net>
    </location>
</configuration>

8. 不要向任何人展示自訂錯誤

如前所述,絕對不要在生產環境中,讓 Web 伺服器從遠端傳回詳細的 ASP.NET 錯誤訊息。

在 microsoft.com 生產和執行環境的根 web.config 檔案中,<configuration><system.web><customErrors> 模式屬性會設定為 RemoteOnly,如此才會向遠端用戶端顯示錯誤訊息,並向 localhost 顯示 ASP.NET 錯誤訊息 (允許 Web 伺服器管理員進行疑難排解)。請注意,<customErrors> 元素是包含在 <location> 標記內,且具有 allowOverride="false" 的值 (請參閱 [圖 3])。這是為了避免個別的應用程式擁有者不小心 (或故意) 設定 mode="Off" 並將詳細的 ASP.NET 錯誤訊息擴散到網際網路上。

Figure 3 避免錯誤訊息顯示

&lt;configuration&gt;
    &lt;location allowOverride='false'&gt;
        &lt;system.web&gt;
            &lt;customErrors mode='RemoteOnly' defaultRedirect=
                   '/errorpages/generic_customerror.aspx'&gt;
                &lt;error statusCode='404' redirect='/errorpages/filenotfound_customerror.aspx' /&gt;
            &lt;/customErrors&gt;
        &lt;/system.web&gt;
    &lt;/location&gt;
&lt;configuration&gt;

同時請記住,如前所述,在 machine.config 檔案中使用 <deployment retail="true"/> 參數會關閉同時對遠端用戶端和本機顯示詳細 ASP.NET 錯誤訊息的功能。如果要執行 ASP.NET 2.0,則這個部署零售參數仍應該是您關閉這些錯誤訊息的主要方法 (若要取得關於 ASP.NET 例外狀況的詳細資訊,請使用應用程式事件記錄)。

在 microsoft.com 對內的預先生產環境的根 web.config 檔案中,<customErrors> 模式屬性是設定為關閉,如此 ASP.NET 錯誤訊息才會永遠同時對本機和遠端用戶端顯示。以下程式碼可用來啟用偵錯和疑難排解,另外,這裡並不會設定任何自訂的錯誤頁面:

<configuration>
    <location allowOverride="false">
        <system.web>
            <customErrors mode="Off" />
        </system.web>
    </location>
<configuration>

9. 知道何時啟用追蹤

在執行 ASP.NET 頁面期間,會產生 ASP.NET 追蹤,並擷取關於 Web 要求、頁面控制項樹狀結構,以及執行頁面生命週期和控制項各種階段的有趣詳細資料。同樣地,您也可以顯示由網頁開發人員寫入追蹤的自訂訊息。該追蹤可以附加至在應用程式的追蹤檢視器中,當作受追蹤要求之清單的一部分而進行追蹤或檢視之頁面的回應輸出。此功能主要是針對內部預先生產環境中的開發階段偵錯案例而提供,不該用於生產部署。

在 microsoft.com 生產和執行環境的根 web.config 檔案中,<configuration><system.web><trace> 啟用的屬性會設定為 "false",如此就可以停用在網頁中輸出追蹤資訊的能力。請注意,<trace> 元素會包含在 <location> 標記內,而且具有 allowOverride="false" 的設定。這是為了避免個別的應用程式擁有者不小心 (或故意) 設定 enabled="true" 並將詳細的 ASP.NET 追蹤資訊輸出到網際網路上。

<configuration>
    <location allowOverride="false">
        <system.web>
              <trace enabled="false" localOnly="true"
              pageOutput="false" requestLimit="10" traceMode="SortByTime" />
        </system.web>
    </location>
<configuration>

<system.web><trace>

如前所述,在 machine.config 檔案中使用 <deployment retail="true"/> 參數也會關閉在網頁中輸出 ASP.NET 追蹤輸出的功能。如果是執行 .NET Framework 2.0,則這個參數應該仍是您關閉追蹤輸出的主要方法。

為了確保無法在對外的生產環境中意外啟用追蹤,我們從 microsoft.com 的根 web.config 檔案移除了實際的 trace.axd 處理常式,或加上註解加以排除,如下所述:

<!--
<add path="trace.axd" verb="*" type=
    "System.Web.Handlers.TraceHandler"
    validate="True" />
-->

10. 停用工作階段狀態 Web 伺服陣列

因為所有 microsoft.com 上的網站目前都是使用不具任何相似性的網域負載平衡 (NLB) 而叢集在一起 (需要不具相似性,才能將要求平均分散到叢集中的所有伺服器),所以不保證同一伺服器會處理指定應用程式的所有要求。結果,ASP.NET 工作階段狀態模組要停用,以避免應用程式開發人員使用 Session 屬性。對於需要維持狀態的應用程式開發人員,我們所提供的指導方針是使用 View State (這樣可以在頁面程式碼內維持結構中的狀態,並且不使用任何伺服器資源)。

若要在 Web 伺服器上停用工作階段狀態,只要從 <httpModules> 節點移除下列的子節點即可:

<add name="Session" type=
    "System.Web.SessionState.
    SessionStateModule"/>

在此我假設您已經看過、甚至使用過 ASP.NET 組態檔 (machine.config、root web.config 以及個別應用程式的 web.config 檔案),但是如果您尚未這麼做,下列的 ASP.NET 快速啟動教學課程將很有幫助:asp.net/QuickStart/aspnet/doc/management/fileformat.aspx (英文)。

本文章提及 ASP.NET 2.0 組態系統現在也包含了一些非常有用的功能,可以讓管理員使用 lockItems 和 lockCollections 屬性來鎖定組態的個別元素和屬性。這些屬性和其用法都記載於下列文章 General Attributes Inherited by Section Elements (英文)。

Jeff Toews 是系統工程主管,已經擔任 Microsoft.com 營運小組的成員六年,目前位於美國華盛頓州的 Redmond。若有任何技術問題或意見,可以透過下列方式與他聯絡:mscomblg@microsoft.com

© 2008 Microsoft Corporation and CMP Media, LLC. 保留所有權利;未經允許,嚴禁部分或全部複製.