保護連接字串與其他設定資訊 (C#)
一般而言,ASP.NET 應用程式會將設定資訊儲存在 Web.config 檔案中。 當中的部分資訊為敏感資訊,且需要保護。 根據預設,這個檔案將不會提供給網站訪客,不過,系統管理員或駭客有機會取得網路伺服器檔案系統的存取權,並檢視檔案內容。 在本教學課程中,您將了解 ASP.NET 2.0 可透過加密 Web.config 檔案的區段,協助我們保護敏感資訊。
簡介
ASP.NET 應用程式的設定資訊通常儲存在名為 Web.config
的 XML 檔案中。 在這一系列的教學課程中,我們已多次更新 Web.config
。 舉例來說,在第一個教學課程中建立 Northwind
具類型資料集時,連接字串資訊會自動新增至 <connectionStrings>
區段的 Web.config
中。 隨後,在主版頁面與網站導覽教學課程中,我們手動更新了 Web.config
、新增了 <pages>
項目來指出專案中的所有 ASP.NET 頁面都應使用 DataWebControls
主題。
由於 Web.config
可能會包含連接字串等敏感資訊,因此,請務必確保 Web.config
的內容安全無虞,且不會向未經授權的檢視者顯示。 根據預設,對於使用 .config
副檔名之檔案的任何 HTTP 要求,都會由 ASP.NET 引擎負責處理,而該引擎會傳回圖 1 所示的 This type of page is not served (未提供這種類型的頁面) 訊息。 這表示,訪客無法透過在瀏覽器網址列中輸入 http://www.YourServer.com/Web.config,以檢視您的 Web.config
檔案內容。
圖 1:透過瀏覽器造訪 Web.config
會傳回「This type of page is not served」(未提供這種類型的頁面) 訊息 (點擊查看完整圖片)
不過,如果攻擊者可找到其他幾個攻擊手法來檢視 Web.config
檔案的內容呢? 攻擊者能如何使用這項資訊?您可以採取哪些步驟來進一步保護 Web.config
中的敏感資訊? 好訊息是,Web.config
中的多數區段都沒有包含敏感資訊。 即使攻擊者知道您 ASP.NET 頁面使用的預設主題名稱,也難以造成傷害。
然而,特定 Web.config
區段則包含敏感資訊,當中可能含有連接字串、使用者名稱、密碼、伺服器名稱和加密金鑰等項目。 通常,這項資訊可在下列 Web.config
區段中取得:
<appSettings>
<connectionStrings>
<identity>
<sessionState>
在本教學課程中,我們將著手探討可保護這類型敏感設定資訊的相關技巧。 我們稍後會看到,.NET Framework 2.0 版包含受保護的設定系統,可輕鬆透過程式設計方式加密和解密所選設定區段。
注意
在本教學課程結束前,我們將提供 Microsoft 對於從 ASP.NET 應用程式連接資料庫所提供的多項建議。 除了加密連接字串外,您也可確保自身的資料庫連接方式是否安全無虞,以協助強化系統。
步驟 1:探索 ASP.NET 2.0 的受保護設定選項
ASP.NET 2.0 包含受保護設定系統,可加密和解密設定資訊。 這也涵蓋 .NET Framework 中可透過程式設計方式加密或解密設定資訊的多個方法。 受保護的設定系統會使用提供者模型,讓開發人員能選擇所使用的密碼編譯實作。
.NET Framework 隨附兩個受保護的設定提供者:
RSAProtectedConfigurationProvider
- 使用非對稱的 RSA algorithm 來加密和解密。DPAPIProtectedConfigurationProvider
- 使用 Windows Data Protection API (DPAPI) 來加密和解密。
由於受保護的設定系統會實作提供者設計模式,因此,您可以建立專屬的受保護設定提供者,並將其插入自身的應用程式中。 如需此程序的詳細資訊,請參閱實作受保護設定提供者。
RSA 和 DPAPI 提供者會使用金鑰來加密和解密常式,而這些金鑰可儲存在機器或使用者層級。 機器層級的金鑰適用於在專屬伺服器上執行 Web 應用程式,或伺服器上有多個需要共用加密資訊的應用程式等情況。 在相同伺服器上的其他應用程式不得解密您應用程式受保護設定區段的情況下,使用者層級的金鑰則可做為更安全的選項。
在本教學課程中,我們的範例將使用 DPAPI 提供者和機器層級的金鑰。 具體而言,儘管受保護設定系統可用於加密絕大部分的 Web.config
區段,我們將著手探討如何加密 Web.config
的 <connectionStrings>
區段。 如需使用使用者層級金鑰或使用 RSA 提供者的詳細資訊,請參閱本教學課程結尾處的<深入閱讀>一節。
注意
RSAProtectedConfigurationProvider
和 DPAPIProtectedConfigurationProvider
提供者會分別在 machine.config
檔案中以提供者名稱 RsaProtectedConfigurationProvider
和 DataProtectionConfigurationProvider
進行註冊。 加密和解密設定資訊時,我們需要提供適當的提供者名稱 (RsaProtectedConfigurationProvider
或 DataProtectionConfigurationProvider
),而非實際的類型名稱 (RSAProtectedConfigurationProvider
和 DPAPIProtectedConfigurationProvider
)。 您可以在 $WINDOWS$\Microsoft.NET\Framework\version\CONFIG
資料夾中找到 machine.config
檔案。
步驟 2:透過程式設計方式加密和解密設定區段
只要透過幾行程式碼,我們就能使用指定的提供者來加密或解密特定設定區段。 稍後即可看到,該程式碼只需透過程式設計方式參考適當的設定區段、呼叫其 ProtectSection
或 UnprotectSection
方法,然後呼叫 Save
方法來儲存變更就好。 此外,.NET Framework 也包含實用的命令列公用程式,可用來加密和解密設定資訊。 我們將在步驟 3 探索這個命令列公用程式。
為了說明如何透過程式設計方式保護設定資訊,我們要建立一個包含按鈕的 ASP.NET 頁面,以加密和解密 Web.config
中的 <connectionStrings>
區段。
首先打開 AdvancedDAL
資料夾中的 EncryptingConfigSections.aspx
頁面。 將 TextBox 控制項從工具箱拖曳到設計器上,然後將其 ID
屬性設為 WebConfigContents
、將 TextMode
屬性設為 MultiLine
,並將 Width
和 Rows
屬性各自設為 95% 和 15。 這個 TextBox 控制項將顯示 Web.config
的內容,好讓我們能快速查看內容是否已加密。 當然,在實際的應用程式中,您可能不會想要顯示 Web.config
的內容。
在 TextBox 下方,新增兩個名為 EncryptConnStrings
和 DecryptConnStrings
的 Button 控制項。 將其 Text 屬性設為 [加密連接字串] 和 [解密連接字串]。
此時,您的畫面看起來應該類似圖 2。
圖 2:在頁面中新增一個 TextBox 和兩個 Button Web 控制項 (按一下以檢視完整大小的影像)
接下來,我們需要撰寫程式碼,以便在首次載入頁面時,載入和顯示 WebConfigContents
TextBox 中的 Web.config
內容。 請將以下程式碼新增至頁面的程式碼後置類別中。 這個程式碼會新增名為 DisplayWebConfig
的方法,並在 Page.IsPostBack
為 false
時,從 Page_Load
事件處理常式呼叫它:
protected void Page_Load(object sender, EventArgs e)
{
// On the first page visit, call DisplayWebConfig method
if (!Page.IsPostBack)
DisplayWebConfig();
}
private void DisplayWebConfig()
{
// Reads in the contents of Web.config and displays them in the TextBox
StreamReader webConfigStream =
File.OpenText(Path.Combine(Request.PhysicalApplicationPath, "Web.config"));
string configContents = webConfigStream.ReadToEnd();
webConfigStream.Close();
WebConfigContents.Text = configContents;
}
DisplayWebConfig
方法會使用 File
類別 開啟 Web.config
檔案,使用 StreamReader
類別 將其內容讀取至字串中,並使用 Path
類別 產生至 Web.config
檔案的實體路徑。 這三個類別都可在 System.IO
命名空間 中找到。 因此,您需要將 using
System.IO
陳述式新增至程式碼後置類別的頂端,或是在這些類別名稱前方加上 System.IO.
。
接著,我們需要為兩個 Button 控制項的 Click
事件新增事件處理常式,並新增必要的程式碼,以使用機器層級的金鑰搭配 DPAPI 提供者來加密和解密 <connectionStrings>
區段。 在設計工具中,按兩下每個 Button,以將 Click
事件處理常式新增至程式碼後置類別中,然後新增下列程式碼:
protected void EncryptConnStrings_Click(object sender, EventArgs e)
{
// Get configuration information about Web.config
Configuration config =
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// Let's work with the <connectionStrings> section
ConfigurationSection connectionStrings = config.GetSection("connectionStrings");
if (connectionStrings != null)
// Only encrypt the section if it is not already protected
if (!connectionStrings.SectionInformation.IsProtected)
{
// Encrypt the <connectionStrings> section using the
// DataProtectionConfigurationProvider provider
connectionStrings.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
config.Save();
// Refresh the Web.config display
DisplayWebConfig();
}
}
protected void DecryptConnStrings_Click(object sender, EventArgs e)
{
// Get configuration information about Web.config
Configuration config =
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// Let's work with the <connectionStrings> section
ConfigurationSection connectionStrings =
config.GetSection("connectionStrings");
if (connectionStrings != null)
// Only decrypt the section if it is protected
if (connectionStrings.SectionInformation.IsProtected)
{
// Decrypt the <connectionStrings> section
connectionStrings.SectionInformation.UnprotectSection();
config.Save();
// Refresh the Web.config display
DisplayWebConfig();
}
}
兩個事件處理常式所使用的程式碼幾乎一模一樣。 兩者都會先透過 WebConfigurationManager
類別 的 OpenWebConfiguration
方法,取得目前應用程式之 Web.config
檔案的相關資訊。 這個方法會傳回指定虛擬路徑的 Web 設定檔案。 隨後,則會透過 Configuration
類別 的 GetSection(sectionName)
方法,存取 Web.config
檔案的 <connectionStrings>
區段,並傳回 ConfigurationSection
物件。
ConfigurationSection
物件包含 SectionInformation
屬性,當中會提供有關設定區段的其他資訊和功能。 如同上方的程式碼所示,我們可檢查 SectionInformation
屬性的 IsProtected
屬性,以判斷設定區段是否已加密。 此外,該區段也可透過 SectionInformation
屬性的 ProtectSection(provider)
和 UnprotectSection
方法來加密或解密。
ProtectSection(provider)
方法將做為輸入字串,指出加密時所使用的受保護設定提供者名稱。 在 Button 的事件處理常式 EncryptConnString
中,我們會將 DataProtectionConfigurationProvider 傳遞至 ProtectSection(provider)
方法,以使用 DPAPI 提供者。 UnprotectSection
方法可判斷用來加密設定區段的提供者,因此不需要任何輸入參數。
呼叫 ProtectSection(provider)
或 UnprotectSection
方法後,您必須呼叫 Configuration
物件的 Save
方法,以保存變更。 在設定資訊以加密或解密,且變更已儲存後,我們將呼叫 DisplayWebConfig
,以將更新的 Web.config
內容載入 TextBox 控制項。
輸入上述程式碼後,請透過瀏覽器造訪 EncryptingConfigSections.aspx
頁面,以進行測試。 首先,您應該會看到列有 Web.config
內容,並以純文字顯示 <connectionStrings>
區段的頁面 (請參閱圖 3)。
圖 3:在頁面中新增一個 TextBox 和兩個 Button Web 控制項 (按一下以檢視完整大小的影像)
現在,請按兩下 [加密連接字串] 按鈕。 如果要求驗證已啟用,從 WebConfigContents
TextBox 回傳的標記就會產生 HttpRequestValidationException
,以顯示「從用戶端偵測到潛在危險的 Request.Form
值」訊息。 ASP.NET 2.0 中預設啟用的要求驗證會禁止包含未編碼 HTML 的回傳內容,且有助於防範指令碼插入式攻擊。 這項檢查可在頁面或應用程式層級停用。 若要為此頁面停用這項檢查,請將 @Page
指示詞中的 ValidateRequest
設定設為 false
。 @Page
指示詞位於頁面宣告標記的頂端。
<%@ Page ValidateRequest="False" ... %>
如需要求驗證的詳細資訊、其用途、如何在頁面和應用程式層級加以停用,以及如何使用 HTML 編碼標記,請參閱要求驗證 - 防止指令碼攻擊。
停用頁面的要求驗證後,請再按一下 [加密連接字串] 按鈕。 回傳時,系統將存取設定檔及其使用 DPAPI 提供者加密的 <connectionStrings>
區段。 隨後則會更新 TextBox,以顯示新的 Web.config
內容。 如圖 4 所示,現在 <connectionStrings>
資訊已加密。
圖 4:按一下 [加密連接字串] 按鈕會加密 <connectionString>
區段 (按一下以檢視完整大小的影像)
後方則會顯示我的電腦上產生的加密 <connectionStrings>
區段,但為求簡潔,<CipherData>
項目中的部分內容已遭到移除:
<connectionStrings
configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/...zChw==</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
注意
<connectionStrings>
項目會指定用來執行加密的提供者 (DataProtectionConfigurationProvider
)。 按一下 [解密連接字串] 按鈕時,UnprotectSection
方法將使用這項資訊。
從 Web.config
存取連接字串資訊 (透過我們撰寫的程式碼、從 SqlDataSource 控制項,或是從我們具類型資料集中的 TableAdapters 自動產生的程式碼) 時,該資訊將自動解密。 簡單來說,我們不需要新增任何額外的程式碼或邏輯,就能解密已加密的 <connectionString>
區段。 為了展示這項特點,現在請造訪先前的任一教學課程,例如<基本報告>一節的「簡單顯示」教學課程 (~/BasicReporting/SimpleDisplay.aspx
)。 如圖 5 所示,教學課程會以我們預期的方式運作,指出 ASP.NET 頁面正在解密已加密的連接字串資訊。
圖 5:資料存取層會自動解密連接字串資訊 (按一下以檢視完整大小的影像)
若要將 <connectionStrings>
區段還原為純文字表示法,請按一下 [解密連接字串] 按鈕。 回傳時,您應該可透過純文字方式檢視 Web.config
中的連接字串。 此時,畫面應該會與我們首次造訪本頁面時相同 (如圖 3 所示)。
步驟 3:使用 aspnet_regiis.exe 加密設定區段
.NET Framework 的 $WINDOWS$\Microsoft.NET\Framework\version\
資料夾中包含許多命令列工具。 舉例來說,在使用 SQL 快取相依性教學課程中,我們探討了如何使用 aspnet_regsql.exe
命令列工具來新增 SQL 快取相依性所需的基礎架構。 這個資料夾中的另一項實用命令列工具,就是 ASP.NET IIS 註冊工具 (aspnet_regiis.exe
)。 如同名稱所示,ASP.NET IIS 註冊工具主要會用來向 Microsoft 的專業級網頁伺服器 IIS 註冊 ASP.NET 2.0 應用程式。 除了與 IIS 相關的功能外,ASP.NET IIS 註冊工具也能用來加密或解密 Web.config
中的指定設定區段。
下列陳述式顯示了用來搭配 aspnet_regiis.exe
命令列工具加密設定區段的一般語法:
aspnet_regiis.exe -pef section physical_directory -prov provider
section 是要加密的設定區段 (例如 connectionStrings )、physical_directory 是 Web 應用程式跟目錄的完整實體路徑,而 provider 則為要使用的受保護設定提供者名稱 (例如 DataProtectionConfigurationProvider )。 或者,如果 Web 應用程式已在 IIS 中註冊,您可以使用下列語法輸入虛擬路徑 (而非實體路徑):
aspnet_regiis.exe -pe section -app virtual_directory -prov provider
下列 aspnet_regiis.exe
範例會使用 DPAPI 提供者搭配機器層級的金鑰,以加密 <connectionStrings>
區段:
aspnet_regiis.exe -pef
"connectionStrings" "C:\Websites\ASPNET_Data_Tutorial_73_CS"
-prov "DataProtectionConfigurationProvider"
同樣地,aspnet_regiis.exe
命令列工具也可用來解密設定區段。 請使用 -pdf
,而非 -pef
參數 (或使用 -pd
,而非 -pe
)。 此外也請注意,解密時無需使用提供者名稱。
aspnet_regiis.exe -pdf section physical_directory
-- or --
aspnet_regiis.exe -pd section -app virtual_directory
注意
由於我們正在使用 DPAPI 提供者,而它會使用電腦專屬的金鑰,因此,您必須從提供網頁的相同的機器執行 aspnet_regiis.exe
。 舉例來說,如果您從本機開發機器執行此命令列程式,然後將加密的 Web.config 檔案上傳至生產伺服器,生產伺服器將無法解密連接字串資訊,因為該資訊是使用您開發機器專屬的金鑰來進行加密。 RSA 提供者則沒有這項限制,原因在於,RSA 金鑰可匯出至另一部機器。
了解資料庫驗證選項
在任何應用程式可向 Microsoft SQL Server 資料庫發出 SELECT
、INSERT
、UPDATE
或 DELETE
查詢之前,資料庫必須先識別要求者。 這項程序又稱為驗證,而 SQL Server 提供兩種驗證方法:
- Windows 驗證 - 應用程式執行的程序會用來與資料庫通訊。 透過 Visual Studio 2005 的 ASP.NET 程式開發伺服器執行 ASP.NET 應用程式時,ASP.NET 應用程式會使用目前已登入使用者的身分識別。 針對 Microsoft Internet Information Server (IIS) 上的 ASP.NET 應用程式,ASP.NET 應用程式通常會使用
domainName``\MachineName
或domainName``\NETWORK SERVICE
的身分識別 (雖然可供自訂)。 - SQL 驗證 - 提供使用者 ID 和密碼值做為驗證的認證機制。 使用 SQL 驗證時,連接字串中會提供使用者 ID 和密碼。
Windows 驗證優於 SQL 驗證,因為其安全性較高。 使用 Windows 驗證時,連接字串不會包含使用者名稱和密碼,如果 Web 伺服器和資料庫伺服器位於兩部不同的機器上,認證將無法透過純文字的形式來使用網路傳送。 而使用 SQL 認證時,連接字串中的驗證認證會進行硬式編碼,並透過純文字的形式,從 Web 伺服器傳送至資料庫伺服器。
這些教學課程皆採用 Windows 驗證。 您可以透過檢查連線字串來判斷目前使用的驗證模式。 針對我們的教學課程,Web.config
中的連接字串為:
Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|\NORTHWND.MDF; Integrated Security=True; User Instance=True
由於 Integrated Security=True,而且缺少使用者名稱和密碼,因此可判斷目前正在使用 Windows 驗證。 部分連接字串中會使用 Trusted Connection=Yes 或 Integrated Security=SSPI 等術語,而非 Integrated Security=True,但三者皆表示目前正在使用 Windows 驗證。
下列範例顯示了使用 SQL 驗證的連接字串。 $CREDENTIAL_PLACEHOLDER$
為索引鍵值組的預留位置。 請注意,認證會內嵌在連接字串中:
Server=serverName; Database=Northwind; uid=userID; $CREDENTIAL_PLACEHOLDER$
假設攻擊者可檢視您應用程式的 Web.config
檔案。 如果您使用 SQL 驗證來連接可透過網際網路存取的資料庫,攻擊者就能使用這個連接字串,透過 SQL Management Studio 或從其網站的 ASP.NET 頁面連至您的資料庫。 若要降低此一威脅,請使用受保護設定系統加密 Web.config
中的連接字串資訊。
注意
如需深入了解 SQL Server 中可供使用的不同驗證類型,請參閱建置安全的 ASP.NET 應用程式:驗證、授權和安全通訊。 如需進一步說明 Windows 和 SQL 驗證語法差異的連接字串範例,請參閱 ConnectionStrings.com。
摘要
根據預設,ASP.NET 應用程式中使用 .config
副檔名的檔案無法透過瀏覽器存取。 這些類型的檔案可能包含敏感資訊 (例如資料庫連接字串、使用者名稱和密碼等),因此不會傳回。 .NET 2.0 中的受保護設定系統可加密特定設定區段,以進一步保護敏感資訊。 當中內建兩個受保護設定提供者:一個會使用 RSA 演算法,另一個則會使用 Windows 資料保護 API (DPAPI)。
在本教學課程中,我們著手探討了如何使用 DPAPI 提供者來加密和解密設定。 這項操作可透過程式設計方式進行 (如同步驟 2 所示),也能透過 aspnet_regiis.exe
命令列工具進行 (如同步驟 3 所示)。 如需使用使用者層級金鑰或改用 RSA 提供者的詳細資訊,請參閱<深入閱讀>一節中的資源。
祝您程式設計愉快!
深入閱讀
如需深入了解本教學課程中探討的多個主題,請參閱下列資源:
- 建置安全的 ASP.NET 應用程式:驗證、授權和安全通訊
- 加密 ASP.NET 2.0 應用程式中的設定資訊
- 加密 ASP.NET 2.0 中的
Web.config
值 - 操作說明:在 ASP.NET 2.0 中使用 DPAPI 加密組態區段
- 操作說明:使用 RSA 加密 ASP.NET 2.0 中的組態區段
- .NET 2.0 中的設定 API
- Windows Data Protection
關於作者
Scott Mitchell,七本 ASP/ASP.NET 書籍的作者和 4GuysFromRolla.com 創始人,自 1998 年以來便開始使用 Microsoft Web 技術。 Scott 擔任獨立顧問、講師和作家。 他的新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 您可以透過 mitchell@4GuysFromRolla.com 或他的部落格 (可以在 http://ScottOnWriting.NET 找到) 與他聯繫。
特別感謝
本教學課程系列已經過許多熱心的檢閱者檢閱。 本教學的主要審閱者是 Teresa Murphy 和 Randy Schmidt。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果有,請發信到 mitchell@4GuysFromRolla.com 。