CodeQL 文档

公开内部表示

ID: java/internal-representation-exposure
Kind: problem
Security severity: 
Severity: recommendation
Precision: high
Tags:
   - reliability
   - maintainability
   - modularity
   - external/cwe/cwe-485
Query suites:
   - java-security-and-quality.qls

点击查看 CodeQL 存储库中的查询

当对象意外地向对象外部的代码公开其内部表示,并且内部表示随后(故意或意外地)以对象无法处理的方式修改时,就会导致一种微妙的缺陷类型。最常见的情况是,当 getter 返回对对象中可变字段的直接引用时,或者 setter 只是将可变参数分配给其字段时。

建议

有三种方法可以解决此问题

  • 使用不可变对象:字段存储的是不可变的对象,这意味着一旦构建,其值就永远不会改变。标准库中的示例有 StringIntegerFloat。尽管此类对象可以是别名,或在多个上下文中共享,但由于无法修改对象,因此不会对对象的内部状态进行意外更改。

  • 创建只读视图java.util.Collections.unmodifiable* 方法可用于创建集合的只读视图,而无需复制它。这往往比创建对象的副本提供更好的性能。请注意,此技术并不适用于所有情况,因为对基础集合的任何更改都会传播到视图。这可能导致意外结果,并且在编写多线程代码时尤其危险。

  • 进行防御性复制:每个 setter(或构造函数)都会对传入参数进行复制或克隆。通过这种方式,它构造了一个仅在内部已知的实例,无论传入的对象发生什么情况,状态都保持一致。相反,字段的每个 getter 也必须构造字段值的副本才能返回。

示例

在以下示例中,私有字段 items 由 getter getItems 直接返回。因此,调用者获取对内部对象状态的引用,并可以操作购物车中的项目集合。在示例中,每次调用 countItems 时,每个购物车都会被清空。

public class Cart {
	private Set<Item> items;
	// ...
	// AVOID: Exposes representation
	public Set<Item> getItems() {
		return items;
	}
}
....
int countItems(Set<Cart> carts) {
	int result = 0;
	for (Cart cart : carts) {
		Set<Item> items = cart.getItems();
		result += items.size();
		items.clear(); // AVOID: Changes internal representation
	}
	return result;
}

解决方案是让 getItems 返回实际字段的副本,例如 return new HashSet<Item>(items);

参考

  • J. Bloch,Effective Java(第二版),第 15 和 39 项。Addison-Wesley,2008 年。

  • Java API 规范:集合

  • 常见弱点枚举:CWE-485

  • ©GitHub,Inc.
  • 条款
  • 隐私