使用 aspnet_regiis 將 web.config 內敏感的資料庫連線字串加密
前言
在開發初期可能不太有人會在連線字串加密議題上打轉,但隨著系統開發進入一個段落,資訊安全的議題就會被放大檢視了,因此對於 web.config 中敏感的連線字串肯定是需要被加密,避免資料庫連線方式被輕易地獲得。針對加密的方式可以選擇自行實作加解密的功能,但就必需要去介入取用連線字串的流程,確保加密後的連線字串可以正常解密取用,而好處就是都是自己實作,想要怎麼做就怎麼做,沒有太多的限制;另外可以考慮微軟提供的加解密方式,如此就不需要特別因為連線字串的加解密功能而調整程式碼,當然越方便的東西限制就可能比較多,因此本篇將針對此方式進行實作。
環境
- IIS v7.5.7600.16385
- Framework v4.0.30319
測試專案
首先建立一個測試專案,加入2組測試用 Connection String 於 Web.config 中,如下所示
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--前略-->
<!--連線字串-->
<connectionStrings>
<add name="BooDB1"
connectionString="Data Source=BooDB1; persist security info=True; initial catalog= Boo; user id=chris;password=bbbb1"
providerName="System.Data.SqlClient" />
<add name="BooDB2"
connectionString="Data Source=BooDB2; persist security info=True; initial catalog= Boo; user id=chris;password=bbbb2"
providerName="System.Data.SqlClient" />
</connectionStrings>
<!--後略-->
</configuration>
然後在 Controller 中取出該些連線字串,存入 ViewBag 中
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.ConnStr1 = ConfigurationManager.ConnectionStrings["BooDB1"].ConnectionString;
ViewBag.ConnStr2 = ConfigurationManager.ConnectionStrings["BooDB2"].ConnectionString;
return View();
}
}
在畫面上顯示剛取出的 ConnectionStrings 來驗證結果是否正確
@{
ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>Connection String</h1>
<p>@ViewBag.ConnStr1</p>
<p>@ViewBag.ConnStr2</p>
</div>
執行後連線字串順利取出,後續將以此作為測試依據。
實作說明
微軟的加密功能會透過 aspnet_regiis.exe
工具來執行,而在介紹加解密方式之前有一件事情一定要知道,就是執行加解密時,預設會使用執行當下的電腦設備之所屬金鑰進行,亦表示若將此加密後的資料佈署到其他電腦時,是無法順利於其他電腦進行解密取得正確資訊,因此需要特別注意加解密執行位置。
以筆者電腦環境為例,可以從以下相對位置找到 aspnet_regiis.exe 工具,因此請先切換至此路徑中。
使用預設RSA金鑰加密
針對單一網站伺服器,如果需要進行web.config加解密,最簡單的方式就是直接在該伺服器中,使用預設RSA金鑰來執行加解密動作。以下介紹如何利用 aspnet_regiis.exe 針對 web.config 進行加解密。
執行加密
執行以上語法後,就會直接將 D:\EncryptConfigWebAppTester\EncryptConfigWebAppTester 中的 Web.config 檔案針對 ConnectionStrings 區塊進行加密。
加密後 Web.config 中 ConnectionStrings 區塊就被加密了
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--前略-->
<!--連線字串-->
<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>qk4npNH9ehz1m7bXQXVWJLyjN7764d9/HYsdMcKquzz4T4dlHaqG05gSAfSdoytqUatxCU5Q5JpF/C4DxfPEx5y5AJi9e6RruJk+EEOEAsEStHne3b+3T672eXC7vAptxY0PAj6RexBiPOzmLYfpVDvFxfnlCDFC1Djl6o/Gzrk=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>GRlZuh2BYgMhX8PqMSARFtX2lRMFB7QRItc+K1hk/p8wuFRV/aBd9PSIiSwaXOf5Ham0H63jHTk8piFce980Gy5WZtOhosJsUPf4UzfvLhHOBnyMZAAMZ5eLFhaqC9pnGsjC9cfQYXnNHrWKwJkHS0WQ36S1EMbUMLJYP0BmcdlQIrZPxuKfsWyWltGMDaXFNoZkY/RjeYIwdvPydnQ/aHdqCaxO5AB2C/GN43mcNT3sD0V+RXxjvRNzkiG0DRW+LqvX2XdWD/W3pSihbxYXjBPLfklOZ/Mz70pofj795cNKICI7qk3955Em0nsSpI0hVwB8k0roVQyx0FpdUgx2BHJv6a24yFO/3qUlr4q67BLXgRMldB0X2ts0yvmCwmUMO/7s/WoW708qIW67mSlEhXYUX2jW7SSfyCXNxkHBTyfXLHiJQGXyWT8KojBDyvrsqAcvkYie0rzr4o3dl9i1W+5c9lMfWOei8GD3AuXbbsvqsQzQiDoN9ATx/Lc6vDc0ZcREDs+I45Vd9cuGJN2fsas79zXaKfo6molD+0TMjVs=</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
<!--後略-->
</configuration>
接著什麼程式都不用修改,直接執行看看;結果確實可以將加密後的連線字串取出。
執行解密
有加密就一定要解密;請執行以上語法,直接將 D:\EncryptConfigWebAppTester\EncryptConfigWebAppTester 中的 Web.config 檔案針對 ConnectionStrings 區塊進行解密。
Web.config 又恢復原始的面貌了
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--前略-->
<!--連線字串-->
<connectionStrings>
<add name="BooDB1"
connectionString="Data Source=BooDB1; persist security info=True; initial catalog= Boo; user id=chris;password=bbbb1"
providerName="System.Data.SqlClient" />
<add name="BooDB2"
connectionString="Data Source=BooDB2; persist security info=True; initial catalog= Boo; user id=chris;password=bbbb2"
providerName="System.Data.SqlClient" />
</connectionStrings>
<!--後略-->
</configuration>
使用相同RSA金鑰加密
筆者先前有提到,加解密預設會使用"當前電腦"中的金鑰進行,如果希望讓所有伺服器都使用相同金鑰進行加解密動作,我們可以先在任一電腦預先建立RSA金鑰容器(Key Container),然後讓其他所有伺服器匯入此金鑰容器,如此就可以讓大家的加解密邏輯(金鑰)一致,詳細步驟請參考以下方式。
Setp 1. 建立共用 RSA 金鑰容器
建立名稱為 ChrisKeys
金鑰容器,並設定允許被匯出。
Setp 2. 在 Web.config 中加註ConfigProtectedData區塊
增加名稱為 ChrisProtectedProvider
的 RsaProtectedConfigurationProvider ,並指定使用先前建立名為 ChrisKeys
的電腦層級 RSA 金鑰容器。
<configProtectedData>
<providers>
<add name="ChrisProtectedProvider"
type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
keyContainerName="ChrisKeys"
useMachineContainer="true" />
</providers>
</configProtectedData>
Setp 3. 使用 ChrisProtectedProvider
來針對 Web.config 中的 ConnectionStrings 加密
加密後Web.config結果如下
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--前略-->
<!--Custom Provider-->
<configProtectedData>
<providers>
<add name="ChrisProtectedProvider"
type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
keyContainerName="ChrisKeys"
useMachineContainer="true" />
</providers>
</configProtectedData>
<!--連線字串-->
<connectionStrings configProtectionProvider="ChrisProtectedProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>kQJ07W23P05CCGusfLkPPeXuRbjD14Wn+8VzqKEQ/tcRbYzZjA1Pwi0QpZ8/p4trXQJw1jfDHS6VmsQTl+3h6GAGqWBWNcaZDS2zCePHw+JJwid8SiQZHrjoBPtN/y4boBtURhBcA0gLcuuMPLc7Ed8h931YkJ6R/+bHSLFB744=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>4rhfEQ6lAJ8G/ER2l0FsEY1MqPquFXKAF0EbV+2gHAIznf0tOZQToVSQ4xNIB33KVzagzmqJP5uSAIWR1Gj3mB3J2JG2FcbIwVauhOQzCk1do3gDLDz8GPNABtxi0D0uSlbeLdLV57tk8EjCkdCDlX5cihR5HNWCQuAgiUgKfDOL2BfvBMYp52Nsbj2ZlC0cd3gefSVO/hiVp5+Oj7oYw7tvVl44YFV9jJtiexuavucEDswUuE+G/z8USD8EN8rMZVHxUdfpmLstF4WojPaYcAM3sITunIUqr0l8dbu+Ahulad/RIcyYIRM5bSPfZdC8TfaUN6ewJ/YazCRVCXOIguFhdf0wy0yhbWbSnqOlc9hiWkkB0ceYbP8MLTZEPcUwqtPCN5zyRBtVqQWU+4908cYhFlaXmfmbRy7jR4pxkH/p9y5pDwUe/sLRgog1P7pxsCtHAjll32njyf8YClIeuVT/Yp1j0ihnmrenAnhxgwuKSq/pFY+zTyNg99+j74ZozACwL+xWzrxpxgfpVV0ojJdpLvdhFnPGTIrZ7gjTXOo=</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
<!--後略-->
</configuration>
直接執行,確實可以將加密後的連線字串取出。
Setp 4. 把 RSA 金鑰容器匯出 (給其他伺服器使用)
Setp 5. 在其他伺服器將 RSA 金鑰容器匯入
Setp 6. 把相同程式連同加密後web.config複製至該伺服器中,直接執行程式。(正確讀出)
Setp 7. 模擬一下佈署上IIS的情況
首先把 使用 IIS Express 取消,並建立虛擬目錄。
執行程式,錯誤發生,無法開啟剛剛匯入的RSA金鑰容器 (權限問題)
來看一下IIS設定,此測試網站是使用 DefaultAppPool 應用程式集區,後續將針對此集區賦予權限。
Setp 8. 賦予帳戶 IIS AppPool\DefaultAppPool
權限來使用 ChrisKeys 金鑰容器
執行下去,燈燈,成功了
參考資訊
http://www.codeproject.com/Articles/877258/How-to-Encrypt-Web-config-Using-aspnet-regiis-exe
http://blog.darkthread.net/post-2010-08-29-web-config-connstr-encryptor-v09-cht.aspx
http://blog.kkbruce.net/2008/09/aspnetwebconfig.html#.VnuZSnF974Y
希望此篇文章可以幫助到需要的人
若內容有誤或有其他建議請不吝留言給筆者喔 !