星期三, 12月 26, 2012

設定檔加密

在 .NET 應用程式或網站,可以利用設定檔(app.config / *.exe.config / Web.config)調整程式的行為,常見的開發環境與執行環境各自有不同的資料連線字串,放在設定檔就很適合。但是接著問題來了,資料連線字串通常含有密碼,如果以明文存放,無疑是對具有伺服器檔案存取權限的人們毫不設防。也許猛一看這沒什麼,畢竟對伺服器可以存取的權限控管本來就應該嚴謹,只是少數人知道而已。但對於一家「所有資料庫都用同一帳號密碼」的資訊服務公司而言,是很高的風險,因為他們在所有客戶端的資料庫,只要有一個洩漏,也就等於所有客戶的資料庫都同時被破。

其實微軟有個基本的加密機制可以運用,我這樣拿來搭配一個應用程式:
  1. 部署到客戶端時,設定檔內的連線字串還是明文。
  2. 在用戶端第一次執行,最開始的程式會檢查設定檔,如果是明文則加密。
  3. 其後每一次執行,最開始的程式仍然會檢查設定檔,如果已加密則不變。
  4. 後續的程式會用到設定檔內容,透過 ConfigurationManager 其實不用為解密煩惱。
  5. 萬一要更改設定,從另一個未加密的「範本設定檔」開始。
看起來不錯?我本來也是這麼想,但要真正了解才會知道這樣的保障是不是足夠。由於我只處理資料連線字串,從範例東抄西剪之後,用下面的程式:

ConnectionStringsSection css = // 取得連線字串段落
css.SectionInformation.ProtectSection("RSAProtectedConfigurationProvider");

引號內的部份是什麼意思?查了半天,總算在 machine.config 得到線索:

<configuration>
    <configProtectedData>
        <providers>
            <add name="RSAProtectedConfigurationProvider" keyContainerName="NetFrameworkConfigurationKey"...

這是一把可用來加解密的金鑰,它可透過以下的命令匯出:

aspnet_regiis -px NetFrameworkConfigurationKey C:\out.xml -pri

但這會有個小問題,「機碼用在特定狀態時無效」,這是什麼?先放著,試試以下這個:

aspnet_regiis -px NetFrameworkConfigurationKey C:\out.xml

就不會有問題。到此學到了一些事:
  1. 系統內可以有很多金鑰,在預設情況下,一把名為「NetFrameworkConfigurationKey」的金鑰可用來加密設定檔。
  2. 這把金鑰是「不可匯出私鑰」的,只是錯誤訊息太糟了。
接著我好奇,在某部電腦上加密的設定檔,可不可以直接複製到另一部電腦上用?當然是搭配著相關金鑰一起移。既然上面那個不可匯出私鑰,只好重新打一把:

aspnet_regiis -pz NetFrameworkConfigurationKey

aspnet_regiis -pc NetFrameworkConfigurationKey -exp

aspnet_regiis -px NetFrameworkConfigurationKey C:\out.xml -pri

將 out.xml 移到另一部機器上:

[Computer2] aspnet_regiis -pi NetFrameworkConfigurationKey C:\out.xml

果然是可用的。