CA2105:陣列欄位不應為唯讀
型別名稱 |
ArrayFieldsShouldNotBeReadOnly |
CheckId |
CA2105 |
分類 |
Microsoft.Security |
中斷變更 |
中斷 |
原因
儲存陣列的公用 (Public) 或保護 (Protected) 的欄位宣告為唯讀。
規則描述
當您將 readonly (在 Visual Basic 中是 ReadOnly) 修飾詞套用至包含陣列的欄位時,欄位就不能變更為參考不同的陣列。但是,儲存在唯讀欄位的陣列元素則可以變更。根據可公開存取的唯讀陣列元素進行決策或執行作業的程式碼,可能會包含可利用的安全性弱點。
請注意,具有公用欄位也會違反設計規則CA1051:不要宣告可見的執行個體欄位。
如何修正違規
若要修正此規則所定義的安全性弱點,請勿依賴可公開存取的唯讀陣列內容。強烈建議您使用下列其中一個程序:
以無法變更的強型別 (Strongly Typed) 集合取代此陣列。如需詳細資訊,請參閱System.Collections.ReadOnlyCollectionBase。
以會傳回私用陣列複製品 (Clone) 的方法取代公用欄位。因為您的程式碼並不依賴此複製品,因此修改元素就不會有危險。
如果您選擇第二種方式,請勿以屬性取代欄位,因為傳回陣列的屬性會對效能造成負面影響。如需詳細資訊,請參閱CA1819:屬性不應傳回陣列。
隱藏警告的時機
強烈建議您排除問題而不是移除規則警告項目。幾乎在所有情況中,唯讀欄位的內容都很重要。如果您也有相同的情況,則請移除 readonly 修飾詞,而非排除訊息。
範例
下列會示範違反這項規則的危險性。第一個部分會顯示具有型別 MyClassWithReadOnlyArrayField 的範例程式庫,其中包含兩個不安全的欄位 (grades 和 privateGrades)。grades 為公用欄位,因此容易遭受任何呼叫端攻擊。雖然 privateGrades 為私用欄位,但因它是由 GetPrivateGrades 方法傳回給呼叫端,所以仍易遭受攻擊。GetSecurePrivateGrades 方法會以安全的方式公開 securePrivateGrades 欄位。此欄位宣告為私用,並且在上述方法中會複製另一份實體將該欄位傳回,此法有遵循良好的設計法則。第二個部分所顯示的程式碼會變更儲存在 grades 和 privateGrades 成員中的值。
下列範例中出現的範例類別庫。
using System;
namespace SecurityRulesLibrary
{
public class MyClassWithReadOnlyArrayField
{
public readonly int[] grades = {90, 90, 90};
private readonly int[] privateGrades = {90, 90, 90};
private readonly int[] securePrivateGrades = {90, 90, 90};
// Making the array private does not protect it because it is passed to others.
public int[] GetPrivateGrades()
{
return privateGrades;
}
//This method secures the array by cloning it.
public int[] GetSecurePrivateGrades()
{
return (int[])securePrivateGrades.Clone();
}
public override string ToString()
{
return String.Format("Grades: {0}, {1}, {2} Private Grades: {3}, {4}, {5} Secure Grades, {6}, {7}, {8}",
grades[0], grades[1], grades[2], privateGrades[0], privateGrades[1], privateGrades[2], securePrivateGrades[0], securePrivateGrades[1], securePrivateGrades[2]);
}
}
}
下列程式碼會使用範例類別庫說明唯讀陣列的安全性問題。
using System;
using SecurityRulesLibrary;
namespace TestSecRulesLibrary
{
public class TestArrayReadOnlyRule
{
[STAThread]
public static void Main()
{
MyClassWithReadOnlyArrayField dataHolder =
new MyClassWithReadOnlyArrayField();
// Get references to the library's readonly arrays.
int[] theGrades = dataHolder.grades;
int[] thePrivateGrades = dataHolder.GetPrivateGrades();
int[] theSecureGrades = dataHolder.GetSecurePrivateGrades();
Console.WriteLine(
"Before tampering: {0}", dataHolder.ToString());
// Overwrite the contents of the "readonly" array.
theGrades[1]= 555;
thePrivateGrades[1]= 555;
theSecureGrades[1]= 555;
Console.WriteLine(
"After tampering: {0}",dataHolder.ToString());
}
}
}
本範例的輸出如下: