共用方式為


DataSet 和 DataTable 安全性指引

本文適用於:

  • .NET Framework (所有版本)
  • .NET Core 和更新版本
  • .NET 5 和更新版本

DataSetDataTable 類型是舊版 .NET 元件,允許將資料集表示為受控物件。 這些元件是在 .NET Framework 1.0 中引進,作為原始 ADO.NET 基礎結構的一部分。 其目標是提供關聯式資料集的受控檢視,摘要出資料的基礎來源是 XML、SQL,還是其他技術。

如需 ADO.NET 的詳細資訊,包括更現代化的資料檢視範例,請參閱 ADO.NET 文件

從 XML 還原序列化 DataSet 或 DataTable 時的預設限制

在所有支援的 .NET Framework、.NET Core 和 .NET 版本上,DataSetDataTable 會對還原序列化資料中可能存在的物件類型設定下列限制。 根據預設,此清單會限制為:

  • 基本類型和基本對等項:boolcharsbytebyteshortushortintuintlongulongfloatdoubledecimalDateTimeDateTimeOffsetTimeSpanstringGuidSqlBinarySqlBooleanSqlByteSqlBytesSqlCharsSqlDateTimeSqlDecimalSqlDoubleSqlGuidSqlInt16SqlInt32SqlInt64SqlMoneySqlSingleSqlString
  • 常用的非基本類型:TypeUriBigInteger
  • 常用的 System.Drawing 類型:ColorPointPointFRectangleRectangleFSizeSizeF
  • Enum 類型。
  • 上述類型的陣列和清單。

如果傳入 XML 資料包含其類型不在這份清單中的物件:

  • 擲回例外狀況,隨附下列訊息和堆疊追蹤。 錯誤訊息:System.InvalidOperationException : 這裡不允許類型 '<Type Name>, Version=<n.n.n.n>, Culture=<culture>, PublicKeyToken=<token value>'。 堆疊追蹤:at System.Data.TypeLimiter.EnsureTypeIsAllowed(Type type, TypeLimiter capturedLimiter) at System.Data.DataColumn.UpdateColumnType(Type type, StorageType typeCode) at System.Data.DataColumn.set_DataType(Type value)

  • 還原序列化作業失敗。

將 XML 載入至現有的 DataSetDataTable 執行個體時,也會考慮現有的資料行定義。 如果資料表已包含自訂類型的資料行定義,該類型會在 XML 還原序列化作業期間暫時新增至允許清單。

注意

一旦您將資料行新增至 DataTableReadXml 就不會從 XML 讀取結構描述,而且如果結構描述不符,其也不會在記錄中讀取,因此您必須自行新增所有資料行,才能使用此方法。

XmlReader xmlReader = GetXmlReader();

// Assume the XML blob contains data for type MyCustomClass.
// The following call to ReadXml fails because MyCustomClass isn't in the allowed types list.

DataTable table = new DataTable("MyDataTable");
table.ReadXml(xmlReader);

// However, the following call to ReadXml succeeds, since the DataTable instance
// already defines a column of type MyCustomClass.

DataTable table = new DataTable("MyDataTable");
table.Columns.Add("MyColumn", typeof(MyCustomClass));
table.ReadXml(xmlReader); // this call will succeed

使用 XmlSerializer 還原序列化 DataSetDataTable 的執行個體時,物件類型限制也適用。 不過,使用 BinaryFormatter 還原序列化 DataSetDataTable 的執行個體時,這些限制可能不適用。

使用 DataAdapter.Fill 時 (例如,直接從資料庫填入 DataTable 執行個體,而不使用 XML 還原序列化 API 時),物件類型限制不適用。

擴充允許的類型清單

除了上面所列的內建類型之外,應用程式還可以擴充允許的類型清單,以包含自訂類型。 如果擴充允許的類型清單,變更會影響應用程式內的所有 DataSetDataTable 執行個體。 無法從內建的允許類型清單中移除類型。

透過組態進行擴充 (.NET Framework 4.0 和更新版本)

App.config 可以用來擴充允許的類型清單。 若要擴充允許的類型清單:

  • 使用 <configSections> 元素,將參考新增至 System.Data 組態區段。
  • 使用 <system.data.dataset.serialization>/<allowedTypes> 來指定其他類型。

每個 <add> 元素必須使用其組件限定類型名稱,僅指定一個類型。 若要將其他類型新增至允許的類型清單,請使用多個 <add> 元素。

下列範例說明藉由新增自訂類型 Fabrikam.CustomType 來擴充允許的類型清單。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="system.data.dataset.serialization" type="System.Data.SerializationSettingsSectionGroup, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <section name="allowedTypes" type="System.Data.AllowedTypesSectionHandler, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
    </sectionGroup>
  </configSections>
  <system.data.dataset.serialization>
    <allowedTypes>
      <!-- <add type="assembly qualified type name" /> -->
      <add type="Fabrikam.CustomType, Fabrikam, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2b3831f2f2b744f7" />
      <!-- additional <add /> elements as needed -->
    </allowedTypes>
  </system.data.dataset.serialization>
</configuration>

若要擷取類型的組件限定名稱,請使用 Type.AssemblyQualifiedName 屬性,如下列程式碼所示範。

string assemblyQualifiedName = typeof(Fabrikam.CustomType).AssemblyQualifiedName;

透過組態進行擴充 (.NET Framework 4.0 和 2.0 - 3.5)

如果您的應用程式以 .NET Framework 2.0 或 3.5 為目標,您仍然可以使用上述 App.config 機制來擴充允許的類型清單。 不過,您的 <configSections> 元素看起來會稍有不同,如下列程式碼所示:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- The below <sectionGroup> and <section> are specific to .NET Framework 2.0 and 3.5. -->
    <sectionGroup name="system.data.dataset.serialization" type="System.Data.SerializationSettingsSectionGroup, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <section name="allowedTypes" type="System.Data.AllowedTypesSectionHandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
    </sectionGroup>
  </configSections>
  <system.data.dataset.serialization>
    <allowedTypes>
      <!-- <add /> elements, as demonstrated in the .NET Framework 4.0 - 4.8 sample code above. -->
    </allowedTypes>
  </system.data.dataset.serialization>
</configuration>

以程序設計方式擴充 (.NET Framework、.NET Core、.NET 5+)

允許的類型清單也可以使用 AppDomain.SetData 搭配已知的機碼 System.Data.DataSetDefaultAllowedTypes,以程式設計方式擴充,如下列程式碼所示。

Type[] extraAllowedTypes = new Type[]
{
    typeof(Fabrikam.CustomType),
    typeof(Contoso.AdditionalCustomType)
};

AppDomain.CurrentDomain.SetData("System.Data.DataSetDefaultAllowedTypes", extraAllowedTypes);

如果使用擴充機制,與索引鍵 System.Data.DataSetDefaultAllowedTypes 相關聯的值,其類型必須是 Type[]

在 .NET Framework 上,可以同時使用 App.configAppDomain.SetData 來擴充允許的類型清單。 在此情況下,如果物件的類型存在於任一清單中,則 DataSetDataTable 將允許該物件進行還原序列化,作為資料的一部分。

以稽核模式執行應用程式 (.NET Framework)

在 .NET Framework 中,DataSetDataTable 提供稽核模式功能。 啟用稽核模式時,DataSetDataTable 會將連入物件的類型與允許的類型清單進行比較。 不過,如果看到不允許其類型的物件,則不會擲回例外狀況。 相反地,DataSetDataTable 會通知任何附加的 TraceListener 執行個體,存在可疑類型,同時允許 TraceListener 記錄這項資訊。 系統不會擲回任何例外狀況,且還原序列化作業會繼續。

警告

以「稽核模式」執行應用程式應該只是用於測試的暫時措施。 啟用稽核模式時,DataSetDataTable 不會強制執行類型限制,這可能會在您的應用程式內引入安全性漏洞。 如需詳細資訊,請參閱標題為移除所有類型限制與不受信任輸入相關的安全章節。

您可以透過 App.config來啟用稽核模式:

  • 如需要為 <configSections> 元素放置適當值的相關資訊,請參閱本文件中的透過組態進行擴充一節。
  • 使用 <allowedTypes auditOnly="true"> 來啟用稽核模式,如下列標記所示。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- See the section of this document titled "Extending through configuration" for the appropriate
         <sectionGroup> and <section> elements to put here, depending on whether you're running .NET
         Framework 2.0 - 3.5 or 4.0 - 4.8. -->
  </configSections>
  <system.data.dataset.serialization>
    <allowedTypes auditOnly="true"> <!-- setting auditOnly="true" enables audit mode -->
      <!-- Optional <add /> elements as needed. -->
    </allowedTypes>
  </system.data.dataset.serialization>
</configuration>

一旦啟用了稽核模式,就可以使用 App.config,將慣用的 TraceListener 連線到 DataSet 內建 TraceSource.。連線追蹤的名稱為 System.Data.DataSet。 下列範例示範如何將追蹤事件寫入主控台,以及寫入磁碟上的記錄檔。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.Data.DataSet"
              switchType="System.Diagnostics.SourceSwitch"
              switchValue="Warning">
        <listeners>
          <!-- write to the console -->
          <add name="console"
               type="System.Diagnostics.ConsoleTraceListener" />
          <!-- *and* write to a log file on disk -->
          <add name="filelog"
               type="System.Diagnostics.TextWriterTraceListener"
               initializeData="c:\logs\mylog.txt" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
</configuration>

如需 TraceSourceTraceListener 的詳細資訊,請參閱作法:搭配追蹤接聽程式使用 TraceSource 和篩選條件一文。

注意

以稽核模式執行應用程式不適用於 .NET Core 或 .NET 5 和更新版本。

移除所有類型限制

如果應用程式必須從 DataSetDataTable 移除所有類型限制:

  • 有數個選項可隱藏類型限制。
  • 可用的選項取決於應用程式的目標架構。

警告

移除所有類型限制可能會在應用程式內引入安全性漏洞。 使用此機制時,請確定應用程式不會 使用 DataSetDataTable 讀取不受信任的輸入。 如需詳細資訊,請參閱 CVE-2020-1147 和下一節,標題為與不受信任輸入相關的安全

透過 AppContext 組態 (.NET Framework 4.6 和更新版本、.NET Core 2.1 和更新版本、.NET 5 和更新版本)

設定為 true 時,AppContext 參數 (Switch.System.Data.AllowArbitraryDataSetTypeInstantiation) 會從 DataSetDataTable 移除所有類型限制。

在 .NET Framework 中,可以透過 App.config 啟用此參數,如下列組態所示:

<configuration>
   <runtime>
      <!-- Warning: setting the following switch can introduce a security problem. -->
      <AppContextSwitchOverrides value="Switch.System.Data.AllowArbitraryDataSetTypeInstantiation=true" />
   </runtime>
</configuration>

在 ASP.NET 中,<AppContextSwitchOverrides> 元素無法使用。 相反地,可以透過 Web.config 啟用參數,如下列組態所示:

<configuration>
    <appSettings>
        <!-- Warning: setting the following switch can introduce a security problem. -->
        <add key="AppContext.SetSwitch:Switch.System.Data.AllowArbitraryDataSetTypeInstantiation" value="true" />
    </appSettings>
</configuration>

如需詳細資訊,請參閱 <AppContextSwitchOverrides> 元素。

在 .NET Core、.NET 5 和 ASP.NET Core 中,此設定是由 runtimeconfig.json 控制,如下列 JSON 所示:

{
  "runtimeOptions": {
    "configProperties": {
      "Switch.System.Data.AllowArbitraryDataSetTypeInstantiation": true
    }
  }
}

如需詳細資訊,請參閱「.NET Core 執行階段組態設定」

AllowArbitraryDataSetTypeInstantiation 也可以透過 AppContext.SetSwitch 以程式設計方式設定,而不是使用組態檔,如下列程式碼所示:

// Warning: setting the following switch can introduce a security problem.
AppContext.SetSwitch("Switch.System.Data.AllowArbitraryDataSetTypeInstantiation", true);

如果您選擇上述的程式設計方法,應該會在應用程式啟動時提早呼叫 AppContext.SetSwitch

透過全電腦登錄 (.NET Framework 2.0 - 4.x)

如果 AppContext 無法使用,則可以使用 Windows 登錄停用類型限制檢查:

  • 管理員必須設定登錄。
  • 使用登錄是全電腦變更,而且會影響電腦上執行的「所有」應用程式。
類型
登錄機碼 HKLM\SOFTWARE\Microsoft\.NETFramework\AppContext
值名稱 Switch.System.Data.AllowArbitraryDataSetTypeInstantiation
值類型 REG_SZ
值資料 true

在 64 位元作業系統上,我需要同時針對 64 位元金鑰 (如上所示) 和 32 位元金鑰新增此值。 32 位元金鑰位於 HKLM\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\AppContext

如需使用登錄來設定 AppContext 的詳細資訊,請參閱「程式庫取用者的 AppContext」

與不受信任輸入相關的安全

雖然 DataSetDataTable 確實會對在還原序列化 XML 承載時允許存在的類型強加預設限制,但 DataSetDataTable 在填入不受信任的輸入時通常不安全。以下是非詳盡清單,列出 DataSetDataTable 執行個體可能讀取不受信任輸入的方式。

  • DataAdapter 會參考資料庫,而且 DataAdapter.Fill 方法用來將資料庫查詢的內容填入 DataSet
  • DataSet.ReadXmlDataTable.ReadXml 方法用來讀取包含資料行和資料列資訊的 XML 檔案。
  • DataSetDataTable 會序列化為 ASP.NET (SOAP) Web 服務或 WCF 端點的一部分。
  • XmlSerializer 這類的序列化程式用來從 XML 資料流還原序列化 DataSetDataTable 執行個體。
  • JsonConvert 這類的序列化程式用來從 JSON 資料流還原序列化 DataSetDataTable 執行個體。 JsonConvert 是著名協力廠商 Newtonsoft.Json 程式庫中的方法。
  • BinaryFormatter 這類的序列化程式用來從原始位元組資料流還原序列化 DataSetDataTable 執行個體。

本文件討論上述案例的安全考量。

使用 DataAdapter.Fill 從不受信任的資料來源填入 DataSet

您可以使用 DataAdapter.Fill 方法DataAdapter 填入 DataSet 執行個體,如下列範例所示。

// Assumes that connection is a valid SqlConnection object.
string queryString =
  "SELECT CustomerID, CompanyName FROM dbo.Customers";
SqlDataAdapter adapter = new SqlDataAdapter(queryString, connection);

DataSet customers = new DataSet();
adapter.Fill(customers, "Customers");

(上述程式碼範例是從 DataAdapter 填入 DataSet 中所找到較大範例的一部分。)

大部分的應用程式都可以簡化並假設其資料庫層受到信任。 不過,如果您習慣對應用程式使用威脅模型化 ,則您的威脅模型可能會認為應用程式 (用戶端) 和資料庫層 (伺服器) 之間會有信任界限。 在用戶端與伺服器之間使用相互驗證AAD 驗證,是協助解決與此相關聯風險的一種方式。 本節的其餘部分會討論用戶端連線到不受信任伺服器的可能結果。

指向不受信任資料來源中 DataAdapter 的結果取決於 DataAdapter 本身的實作。

SqlDataAdapter

針對內建類型 SqlDataAdapter,參考不受信任的資料來源可能會導致拒絕服務 (DoS) 攻擊。 DoS 攻擊可能會導致應用程式沒有回應或當機。 如果攻擊者可以在應用程式旁邊植入 DLL,他們也能夠實現本機程式碼執行。

其他 DataAdapter 類型

協力廠商 DataAdapter 實作必須自行評估他們面對不受信任輸入時所提供的安全性保證。 .NET 無法針對這些實作做出任何安全保證。

DataSet.ReadXml 和 DataTable.ReadXml

與不受信任的輸入搭配使用時,DataSet.ReadXmlDataTable.ReadXml 方法並不安全。 強烈建議取用者改為考慮使用本文件稍後所述的其中一個替代方案。

DataSet.ReadXmlDataTable.ReadXml 的實作最初是在序列化弱點為眾所周知威脅類別之前所建立。 因此,程式碼不會遵循目前的安全性最佳做法。 這些 API 可以用作攻擊者針對 Web 應用程式執行 DoS 攻擊的媒介。 這些攻擊可能會使 Web 服務沒有回應,或導致非預期的處理序終止。 此架構不會為這些攻擊類別提供風險降低措施,而且 .NET 會將此行為視為「設計」所致。

.NET 已發行安全性更新,以緩解 DataSet.ReadXmlDataTable.ReadXml 中的資訊洩漏或遠端程式碼執行等一些問題。 .NET 安全性更新可能無法針對這些威脅類別提供完整保護。 取用者應該評估其個別案例,並考慮到他們可能暴露在這些風險中。

取用者應該注意,這些 API 的安全性更新在某些情況下可能會影響應用程式相容性。 此外,可能會在這些 API 中探索到新型弱點,但實際上 .NET 無法為這些弱點發佈安全性更新。

建議這些 API 的取用者:

  • 考慮使用本文件稍後所述的其中一個替代方案。
  • 在其應用程式上執行個別風險評估。

取用者必須負責判斷是否要使用這些 API。 取用者應該評估使用這些 API 可能伴隨的任何安全性、技術和法律風險,包括法規需求。

透過 ASP.NET Web 服務或 WCF 的 DataSet 和 DataTable

可以接受 ASP.NET (SOAP) Web 服務中的 DataSetDataTable 執行個體,如下列程式碼所示:

using System.Data;
using System.Web.Services;

[WebService(Namespace = "http://contoso.com/")]
public class MyService : WebService
{
    [WebMethod]
    public string MyWebMethod(DataTable dataTable)
    {
        /* Web method implementation. */
    }
}

此情況的變體不是直接接受 DataSetDataTable 作為參數,而是改為接受其作為整體 SOAP 序列化物件圖形的一部分,如下列程式碼所示:

using System.Data;
using System.Web.Services;

[WebService(Namespace = "http://contoso.com/")]
public class MyService : WebService
{
    [WebMethod]
    public string MyWebMethod(MyClass data)
    {
        /* Web method implementation. */
    }
}

public class MyClass
{
    // Property of type DataTable, automatically serialized and
    // deserialized as part of the overall MyClass payload.
    public DataTable MyDataTable { get; set; }
}

或者,使用 WCF 而不是 ASP.NET Web 服務:

using System.Data;
using System.ServiceModel;

[ServiceContract(Namespace = "http://contoso.com/")]
public interface IMyContract
{
    [OperationContract]
    string MyMethod(DataTable dataTable);
    [OperationContract]
    string MyOtherMethod(MyClass data);
}

public class MyClass
{
    // Property of type DataTable, automatically serialized and
    // deserialized as part of the overall MyClass payload.
    public DataTable MyDataTable { get; set; }
}

在所有這些情況下,威脅模型和安全性保證與 DataSet.ReadXml 和 DataTable.ReadXml 區段相同。

透過 XmlSerializer 還原序列化 DataSet 或 DataTable

開發人員可以使用 XmlSerializer 還原序列化 DataSetDataTable 執行個體,如下列程式碼所示:

using System.Data;
using System.IO;
using System.Xml.Serialization;

public DataSet PerformDeserialization1(Stream stream) {
    XmlSerializer serializer = new XmlSerializer(typeof(DataSet));
    return (DataSet)serializer.Deserialize(stream);
}

public MyClass PerformDeserialization2(Stream stream) {
    XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
    return (MyClass)serializer.Deserialize(stream);
}

public class MyClass
{
    // Property of type DataTable, automatically serialized and
    // deserialized as part of the overall MyClass payload.
    public DataTable MyDataTable { get; set; }
}

在這些情況下,威脅模型和安全性保證與 DataSet.ReadXml 和 DataTable.ReadXml 區段相同

透過 JsonConvert 還原序列化 DataSet 或 DataTable

著名協力廠商 Newtonsoft 程式庫 Json.NET 可以用來還原序列化 DataSetDataTable 執行個體,如下列程式碼所示:

using System.Data;
using Newtonsoft.Json;

public DataSet PerformDeserialization1(string json) {
    return JsonConvert.DeserializeObject<DataSet>(data);
}

public MyClass PerformDeserialization2(string json) {
    return JsonConvert.DeserializeObject<MyClass>(data);
}

public class MyClass
{
    // Property of type DataTable, automatically serialized and
    // deserialized as part of the overall MyClass payload.
    public DataTable MyDataTable { get; set; }
}

以這種方式從不受信任的 JSON Blob 還原序列化 DataSetDataTable 並不安全。 這種模式很容易遭受拒絕服務的攻擊。 這類攻擊可能會損毀應用程式,或使其沒有回應。

注意

Microsoft 不保證或支援協力廠商程式庫的實作,例如 Newtonsoft.Json。 這項資訊是針對完整性而提供,且在撰寫本文時正確無誤。

透過 BinaryFormatter 還原序列化 DataSet 或 DataTable

您絕不能使用 BinaryFormatterNetDataContractSerializerSoapFormatter 或相關的不安全格式器,從不受信任的承載還原序列化 DataSetDataTable 執行個體:

  • 這很容易遭受完整的遠端程式碼執行攻擊。
  • 使用自訂 SerializationBinder 並不足以防止這類攻擊。

安全取代

針對下列情況的應用程式:

  • 透過 .asmx SOAP 端點或 WCF 端點接受 DataSetDataTable
  • 將不受信任的資料還原序列化為 DataSetDataTable 的執行個體。

考慮取代物件模型以使用 Entity Framework。 Entity Framework:

  • 這是一種豐富的現式物件導向架構,可以代表關聯式資料。
  • 帶來資料庫提供者的各種生態系統,讓您能夠輕鬆地透過 Entity Framework 物件模型來投影資料庫查詢。
  • 從不受信任的來源還原序列化資料時提供內建保護。

對於使用 .aspx SOAP 端點的應用程式,請考慮將這些端點變更為使用 WCF。 WCF 是取代 .asmx Web 服務且功能更完整的項目。 WCF 端點可以透過 SOAP 公開,以與現有的呼叫端相容。

程式碼分析器

在原始程式碼編譯時執行的程式碼分析器安全性規則,有助於在 C# 和 Visual Basic 程式碼中尋找與這個安全性問題相關的弱點。 Microsoft.CodeAnalysis.FxCopAnalyzers 是散發在 nuget.org 上的 NuGet 程式碼分析器套件。

如需程式碼分析器的概觀,請參閱原始程式碼分析器概觀

啟用下列 Microsoft.CodeAnalysis.FxCopAnalyzers 規則:

如需設定規則的詳細資訊,請參閱使用程式碼分析器

新的安全性規則可在下列 NuGet 套件中取得:

  • Microsoft.CodeAnalysis.FxCopAnalyzers 3.3.0:適用於 Visual Studio 2019 16.3 版或更新版本
  • Microsoft.CodeAnalysis.FxCopAnalyzers 2.9.11:適用於 Visual Studio 2017 15.9 版或更新版本