公开内部表示¶
ID: cs/expose-implementation
Kind: problem
Security severity:
Severity: recommendation
Precision: high
Tags:
- reliability
- external/cwe/cwe-485
Query suites:
- csharp-security-and-quality.qls
如果类的方法返回对可变私有字段的引用,则该方法有时会将内部字段暴露给其他代码进行更改。
建议¶
根据您的情况,有几种方法可以解决此问题。最佳方法之一是使用不可变对象来存储字段。可以将对此对象的引用传递到类外部,但对象是不可变的,因此无法更改。
另一种防止外部修改私有字段的好方法是仅返回字段引用的对象的副本。这称为“防御性复制”。如果副本发生更改,则内部字段不会受到影响。
示例¶
此示例清楚地说明了将对可变对象的引用传递到类外部的问题。在这种情况下,尽管 Range
类未提供任何方法,但可以修改数组中的值。
using System;
class Bad
{
class Range
{
private int[] rarray = new int[2];
public Range(int min, int max)
{
if (min <= max)
{
rarray[0] = min;
rarray[1] = max;
}
}
public int[] Get() => rarray;
}
public static void Main(string[] args)
{
var r = new Range(1, 10);
var r_range = r.Get();
r_range[0] = 500;
Console.WriteLine("Min: " + r.Get()[0] + " Max: " + r.Get()[1]);
// prints "Min: 500 Max: 10"
}
}
使用不可变对象修复¶
此处已修改示例,以通过使用 ReadOnlyCollection
对象来防止对私有字段的更改。
using System.Collections.ObjectModel;
class Good1
{
class Range
{
private ReadOnlyCollection<int> rarray = new ReadOnlyCollection<int>(new int[2]);
public Range(int min, int max)
{
if (min <= max)
{
int[] rarray = new int[2];
rarray[0] = min;
rarray[1] = max;
this.rarray = new ReadOnlyCollection<int>(rarray);
}
}
public ReadOnlyCollection<int> Get() => rarray;
}
}
使用防御性复制修复¶
这是同一类的示例,但这次它返回私有字段的防御性副本。还有一个简短的程序,显示了尝试修改字段保存的数据时会发生什么。
using System;
class Good2
{
class Range
{
private int[] rarray = new int[2];
public Range(int min, int max)
{
if (min <= max)
{
rarray[0] = min;
rarray[1] = max;
}
}
public int[] Get() => (int[])rarray.Clone();
}
public static void Main(string[] args)
{
Range a = new Range(1, 10);
int[] a_range = a.Get();
a_range[0] = 500;
Console.WriteLine("Min: " + a.Get()[0] + " Max: " + a.Get()[1]);
// prints "Min: 1 Max: 10"
}
}
参考资料¶
MSDN,C# 编程指南,数组作为对象。
MSDN,ReadOnlyCollection<T>。
常见弱点枚举:CWE-485。