連接字串與組態檔

在應用程式的程式碼中嵌入連接字串可能會導致安全性漏洞和維護問題。 編譯到應用程式原始程式碼中的未加密連接字串,可使用 Ildasm.exe (IL 反組譯工具) 工具進行檢視。 此外,如果連接字串變更,應用程式就必須重新編譯。 基於上述理由,建議您將連接字串儲存在應用程式組態檔中。

使用應用程式組態檔

應用程式組態檔包含特定應用程式專屬的設定。 例如,ASP.NET 應用程式可能擁有一或多個 web.config 檔案,Windows 應用程式則可能具有選擇性的 app.config 檔案。 組態檔會共用通用的項目,但組態檔的名稱及位置則會根據應用程式的主機而不同。

connectionStrings 區段

連接字串可以用索引鍵/值對的方式,儲存在應用程式組態檔 configuration 項目的 connectionStrings 區段中。 子項目包含 addclearremove

下列組態檔片段示範儲存連接字串的結構描述和語法。 name 屬性是用於唯一識別連接字串的名稱,可藉此在執行階段擷取該連接字串。 providerName 是 .NET Framework 資料提供者的非變異名稱,登錄於 machine.config 檔案。

<?xml version='1.0' encoding='utf-8'?>  
  <configuration>  
    <connectionStrings>  
      <clear />  
      <add name="Name"
       providerName="System.Data.ProviderName"
       connectionString="Valid Connection String;" />  
    </connectionStrings>  
  </configuration>  

注意

您可將部分連接字串儲存在組態檔中,然後在執行階段使用 DbConnectionStringBuilder 類別 (Class) 加以完成。 在您無法預先知道連接字串的項目,或者不想將機密資訊儲存在組態檔中時,這種方法很有用。 如需詳細資訊,請參閱連接字串建置器

使用外部組態檔

外部組態檔是包含組態檔片段 (由單一區段組成) 的個別檔案。 外部組態檔接著會由主組態檔來參考。 將 connectionStrings 區段儲存在實際分開的檔案中,對於在部署應用程式之後可能會編輯連接字串的情況很有用。 例如,標準的 ASP.NET 行為是在組態檔修改時重新啟動應用程式網域,而這可能導致狀態資訊遺失。 然而,修改外部組態檔並不會造成應用程式重新啟動。 外部組態檔並不僅限於 ASP.NET 才有,Windows 應用程式也可加以利用; 此外,也可以透過檔案存取安全性和權限,限制對外部組態檔的存取權。 執行階段的外部組態檔使用是透明的,而且不需要任何特殊的程式碼。

若要將連接字串儲存於外部組態檔,請建立僅包含 connectionStrings 區段的個別檔案。 請勿包含任何額外的項目、區段或屬性。 此範例說明外部組態檔的語法。

<connectionStrings>  
  <add name="Name"
   providerName="System.Data.ProviderName"
   connectionString="Valid Connection String;" />  
</connectionStrings>  

在主應用程式組態檔中,可以使用 configSource 屬性來指定外部檔案的完整名稱及位置。 此範例會參考名為 connections.config 的外部組態檔。

<?xml version='1.0' encoding='utf-8'?>  
<configuration>  
    <connectionStrings configSource="connections.config"/>  
</configuration>  

在執行階段擷取連接字串

.NET Framework 2.0 在 System.Configuration 命名空間 (Namespace) 中導入了新的類別,可簡化在執行階段從組態檔中擷取連接字串的作業。 您可以透過程式設計的方式,依名稱或提供者名稱擷取連接字串。

注意

machine.config 檔案也包含 connectionStrings 區段,後者則包含 Visual Studio 所使用的連接字串。 從 Windows 應用程式中的 app.config 檔案依提供者名稱擷取連接字串時,首先會載入 machine.config 中的連接字串,然後才會載入 app.config 的項目。如果在 connectionStrings 元素之後緊接著新增 clear,就會從記憶體中的資料結構移除所有繼承的參考,如此便只會考量本機 app.config 檔案中所定義的連接字串。

使用組態類別

從 .NET Framework 2.0 開始,在本機電腦上使用組態檔時,就會使用 ConfigurationManager 來取代已被取代的 ConfigurationSettingsWebConfigurationManager 則會用於搭配 ASP.NET 組態檔。 這是為了在 Web 伺服器上使用組態檔而設計,可透過程式設計的方式存取 system.web 之類的組態檔區段。

注意

您必須為呼叫端授與權限,才能在執行階段存取組態檔;所需的權限則根據應用程式類型、組態檔以及位置而有所不同。 如需詳細資訊,請參閱使用組態類別WebConfigurationManager (適用於 ASP.NET 應用程式) 以及 ConfigurationManager (適用於 Windows 應用程式)。

您可以利用 ConnectionStringSettingsCollection 從應用程式組態檔擷取連接字串。 此項目包含 ConnectionStringSettings 物件的集合,而其中每個物件都代表 connectionStrings 區段中的單一項目。 其屬性會對應至連接字串屬性,讓您可以藉由指定名稱或提供者名稱而擷取連接字串。

屬性 描述
Name 連接字串的名稱。 對應至 name 屬性。
ProviderName 完整的提供者名稱。 對應至 providerName 屬性。
ConnectionString 連接字串。 對應至 connectionString 屬性。

範例:列出所有連接字串

此範例會逐一查看 ConnectionStringSettingsCollection,並在主控台視窗中顯示 ConnectionStringSettings.NameConnectionStringSettings.ProviderNameConnectionStringSettings.ConnectionString 屬性。

注意

System.Configuration.dll 並未包含在所有專案類型中,您可能需要設定其參考,才能使用該組態類別。 特定應用程式組態檔的名稱和位置會根據應用程式類型及裝載處理序而不同。

using System.Configuration;

static class Program
{
    static void Main()
    {
        GetConnectionStrings();
        Console.ReadLine();
    }

    static void GetConnectionStrings()
    {
        ConnectionStringSettingsCollection settings =
            ConfigurationManager.ConnectionStrings;

        foreach (ConnectionStringSettings cs in settings)
        {
            Console.WriteLine(cs.Name);
            Console.WriteLine(cs.ProviderName);
            Console.WriteLine(cs.ConnectionString);
        }
    }
}
Imports System.Configuration

Class Program
    Shared Sub Main()
        GetConnectionStrings()
        Console.ReadLine()
    End Sub

    Private Shared Sub GetConnectionStrings()

        Dim settings As ConnectionStringSettingsCollection = _
            ConfigurationManager.ConnectionStrings

        If Not settings Is Nothing Then
            For Each cs As ConnectionStringSettings In settings
                Console.WriteLine(cs.Name)
                Console.WriteLine(cs.ProviderName)
                Console.WriteLine(cs.ConnectionString)
            Next
        End If
    End Sub
End Class

範例:依名稱擷取連接字串

此範例示範如何藉由指定連接字串的名稱,從組態檔擷取連接字串。 程式碼會建立 ConnectionStringSettings 物件,並將提供的輸入參數與 ConnectionStrings 名稱進行比對。 如果沒有找到符合的名稱,函式就會傳回 null (在 Visual Basic 中則為 Nothing)。

// Retrieves a connection string by name.
// Returns null if the name is not found.
static string? GetConnectionStringByName(string name)
{
    // Look for the name in the connectionStrings section.
    ConnectionStringSettings? settings =
        ConfigurationManager.ConnectionStrings[name];

    // If found, return the connection string (otherwise return null)
    return settings?.ConnectionString;
}
' Retrieves a connection string by name.
' Returns Nothing if the name is not found.
Private Shared Function GetConnectionStringByName( _
    ByVal name As String) As String

    ' Assume failure
    Dim returnValue As String = Nothing

    ' Look for the name in the connectionStrings section.
    Dim settings As ConnectionStringSettings = _
       ConfigurationManager.ConnectionStrings(name)

    ' If found, return the connection string.
    If Not settings Is Nothing Then
        returnValue = settings.ConnectionString
    End If

    Return returnValue
End Function

範例:依提供者名稱擷取連接字串

此範例示範如何藉由指定 System.Data.ProviderName 格式的提供者非變異名稱來擷取連接字串。 程式碼會逐一查看 ConnectionStringSettingsCollection,並針對第一個找到的 ProviderName 傳回連接字串。 如果找不到提供者名稱,函式就會傳回 null (在 Visual Basic 中則為 Nothing)。

// Retrieve a connection string by specifying the providerName.
// Assumes one connection string per provider in the config file.
static string? GetConnectionStringByProvider(string providerName)
{
    // Get the collection of connection strings.
    ConnectionStringSettingsCollection? settings =
        ConfigurationManager.ConnectionStrings;

    // Walk through the collection and return the first
    // connection string matching the providerName.
    if (settings != null)
    {
        foreach (ConnectionStringSettings cs in settings)
        {
            if (cs.ProviderName == providerName)
            {
                return cs.ConnectionString;
            }
        }
    }
    return null;
}
' Retrieve a connection string by specifying the providerName.
' Assumes one connection string per provider in the config file.
Private Shared Function GetConnectionStringByProvider( _
    ByVal providerName As String) As String

    'Return Nothing on failure.
    Dim returnValue As String = Nothing

    ' Get the collection of connection strings.
    Dim settings As ConnectionStringSettingsCollection = _
        ConfigurationManager.ConnectionStrings

    ' Walk through the collection and return the first 
    ' connection string matching the providerName.
    If Not settings Is Nothing Then
        For Each cs As ConnectionStringSettings In settings
            If cs.ProviderName = providerName Then
                returnValue = cs.ConnectionString
                Exit For
            End If
        Next
    End If

    Return returnValue
End Function

使用受保護的組態來加密組態檔區段

ASP.NET 2.0 導入一項稱為「受保護的組態」的新功能,可用於加密組態檔中的機密資訊。 雖然主要是針對 ASP.NET 所設計,但這項功能仍可用來加密 Windows 應用程式中的組態檔區段。 如需受保護之組態功能的詳細描述,請參閱使用受保護的組態加密組態資訊

下列組態檔片段顯示加密之後的 connectionStrings 區段。 configProtectionProvider 會指定用於加密及解密連接字串的受保護組態提供者。 EncryptedData 區段包含加密文字。

<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">  
  <EncryptedData>  
    <CipherData>  
      <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAH2... </CipherValue>  
    </CipherData>  
  </EncryptedData>  
</connectionStrings>  

在執行階段擷取加密的連接字串時,.NET Framework 會使用指定的提供者對 CipherValue 進行解密,並將其提供給應用程式使用。 您不需要撰寫任何額外的程式碼來管理解密程序。

受保護的組態提供者

受保護的組態提供者會在本機電腦上登錄於 machine.config 檔案的 configProtectedData 區段,如下列片段所示,此片段顯示 .NET Framework 所提供的兩個受保護的組態提供者。 為了便於讀取,這裡顯示的值已經過刪減。

<configProtectedData defaultProvider="RsaProtectedConfigurationProvider">  
  <providers>  
    <add name="RsaProtectedConfigurationProvider"
      type="System.Configuration.RsaProtectedConfigurationProvider" />  
    <add name="DataProtectionConfigurationProvider"
      type="System.Configuration.DpapiProtectedConfigurationProvider" />  
  </providers>  
</configProtectedData>  

您可以將額外的受保護組態提供者新增至 machine.config 檔案來進行設定。 也可以從 ProtectedConfigurationProvider 抽象基底類別 (Base Class) 繼承而建立自己的受保護組態提供者。 下表說明 .NET Framework 所隨附的兩個組態檔。

提供者 描述
RsaProtectedConfigurationProvider 使用 RSA 加密演算法來加密及解密資料。 RSA 演算法可用於公開金鑰 (Public Key) 加密及數位簽章。 這種演算法也稱為「公開金鑰」或非對稱加密,因為它會使用兩種不同的金鑰。 您可以使用 ASP.NET IIS 註冊工具 (Aspnet_regiis.exe) 來加密 Web.config 檔案中的區段並管理加密金鑰。 ASP.NET 會在處理檔案時對組態檔進行解密。 ASP.NET 應用程式的識別必須可以讀取用於對區段進行加密及解密的加密金鑰。
DpapiProtectedConfigurationProvider 使用 Windows Data Protection API (DPAPI) 來加密組態區段。 DPAPI 使用 Windows 內建的密碼編譯服務,可以針對電腦特定或使用者帳戶特定的保護進行設定。 電腦特定的保護特別適用於相同伺服器上需要共用資訊的多個應用程式。 使用者特定的保護則可用於使用特定使用者識別執行的服務,例如共用的裝載環境。 每個應用程式都會在不同的識別之下執行,如此可限制對檔案和資料庫等資源的存取。

這兩種提供者都提供高度加密的資料。 不過,如果您打算在多個伺服器 (例如 Web 伺服陣列) 上使用相同的加密組態檔,則只有使用 RsaProtectedConfigurationProvider 才能匯出用於加密資料的加密金鑰並將其匯入另一個伺服器。 如需詳細資訊,請參閱匯入和匯出受保護的組態 RSA 金鑰容器

使用組態類別

System.Configuration 命名空間 (Namespace) 提供類別 (Class),以透過程式設計的方式使用組態設定。 ConfigurationManager 類別可用於存取電腦、應用程式及使用者組態檔。 如果要建立 ASP.NET 應用程式,則可以使用 WebConfigurationManager 類別,此類別不但提供相同的功能,還可用於存取 ASP.NET 應用程式獨有的設定,例如 <system.web> 中的設定。

注意

System.Security.Cryptography 命名空間包含可為資料加密及解密提供額外選項的類別。 如果需要無法使用受保護組態而提供的密碼編譯服務,請使用這些類別。 這其中某些類別是 Unmanaged Microsoft CryptoAPI 的包裝函式,某些則純粹是 Managed 實作 (Implementation)。 如需詳細資訊,請參閱密碼編譯服務

App.config 範例

此範例示範如何在 Windows 應用程式的 app.config 檔案中切換 connectionStrings 區段的加密。 在此範例中,程序會採用應用程式的名稱做為引數,例如 "MyApplication.exe"。 接下來會加密 app.config 檔案,並將其複製到 "MyApplication.exe.config" 名稱下包含可執行檔的資料夾。

注意

連接字串只能在當初進行加密的電腦上進行解密。

程式碼會使用 OpenExeConfiguration 方法來開啟 app.config 檔案進行編輯,GetSection 方法則會傳回 connectionStrings 區段。 接著程式碼會檢查 IsProtected 屬性,並呼叫 ProtectSection 來加密區段 (如果尚未加密), 然後再叫用 UnprotectSection 方法來對區段進行解密。 Save 方法則會完成作業並儲存變更。

注意

您必須在專案中設定 System.Configuration.dll 的參考,程式碼才能執行。

static void ToggleConfigEncryption(string exeFile)
{
    // Get the application path needed to obtain
    // the application configuration file.

    // Takes the executable file name without the
    // .config extension.
    var exePath = exeFile.Replace(".config", "");

    try
    {
        // Open the configuration file and retrieve
        // the connectionStrings section.
        Configuration config = ConfigurationManager.
            OpenExeConfiguration(exePath);

        var section =
            config.GetSection("connectionStrings")
            as ConnectionStringsSection;

        if (section != null)
        {
            if (section.SectionInformation.IsProtected)
            {
                // Remove encryption.
                section.SectionInformation.UnprotectSection();
            }
            else
            {
                // Encrypt the section.
                section.SectionInformation.ProtectSection(
                    "DataProtectionConfigurationProvider");
            }
        }
        // Save the current configuration.
        config.Save();

        Console.WriteLine("Protected={0}",
            section?.SectionInformation.IsProtected);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
Shared Sub ToggleConfigEncryption(ByVal exeConfigName As String)
    ' Takes the executable file name without the
    ' .config extension.
    Try
        ' Open the configuration file and retrieve 
        ' the connectionStrings section.
        Dim config As Configuration = ConfigurationManager. _
            OpenExeConfiguration(exeConfigName)

        Dim section As ConnectionStringsSection = DirectCast( _
            config.GetSection("connectionStrings"), _
            ConnectionStringsSection)

        If section.SectionInformation.IsProtected Then
            ' Remove encryption.
            section.SectionInformation.UnprotectSection()
        Else
            ' Encrypt the section.
            section.SectionInformation.ProtectSection( _
              "DataProtectionConfigurationProvider")
        End If

        ' Save the current configuration.
        config.Save()

        Console.WriteLine("Protected={0}", _
        section.SectionInformation.IsProtected)

    Catch ex As Exception
        Console.WriteLine(ex.Message)
    End Try
End Sub

Web.config 範例

此範例使用 OpenWebConfigurationWebConfigurationManager 方法。 請注意,在此例中可以藉由波狀符號提供 Web.config 檔案的相對路徑。 程式碼需要 System.Web.Configuration 類別的參考。

static void ToggleWebEncrypt()
{
    // Open the Web.config file.
    Configuration config = WebConfigurationManager.
        OpenWebConfiguration("~");

    // Get the connectionStrings section.
    var section =
        config.GetSection("connectionStrings")
        as ConnectionStringsSection;

    // Toggle encryption.
    if (section.SectionInformation.IsProtected)
    {
        section.SectionInformation.UnprotectSection();
    }
    else
    {
        section.SectionInformation.ProtectSection(
            "DataProtectionConfigurationProvider");
    }

    // Save changes to the Web.config file.
    config.Save();
}
Shared Sub ToggleWebEncrypt()
    ' Open the Web.config file.
    Dim config As Configuration = WebConfigurationManager. _
      OpenWebConfiguration("~")

    ' Get the connectionStrings section.
    Dim section As ConnectionStringsSection = DirectCast( _
        config.GetSection("connectionStrings"), _
        ConnectionStringsSection)

    ' Toggle encryption.
    If section.SectionInformation.IsProtected Then
        section.SectionInformation.UnprotectSection()
    Else
        section.SectionInformation.ProtectSection( _
          "DataProtectionConfigurationProvider")
    End If

    ' Save changes to the Web.config file.
    config.Save()
End Sub

如需保護 ASP.NET 應用程式的詳細資訊,請參閱保護 ASP.NET 網站

另請參閱