Effective C# Item47:选择安全代码
.NET运行时在设计之初,就堵住了远程机器上恶意代码渗透和执行的漏洞,但是一些分布式系统总要从远程机器上下载并执行代码。如果CLR不能完全信任一个程序集,它将会限制在其上可以操作的行为,这种机制被称作代码访问安全(code access security,CAS),另一方面,CLR实现了基于角色的安全机制,在这种机制下,代码执行与否要依赖于特定用户账号和权限。
.NET是一个托管环境,它会确保在一定程度上的安全,.NET框架类库中的大多数类型在安装时都被赋予了完全的信任(通过.NET配置策略),这些类型都是经过安全验证的,这意味着CLR会检查IL并确保它们不会执行任何潜在危险的操作,它们也不需要任何特殊的安全许可来访问本地资源。我们在写代码时也应该尽可能遵循同样的策略,如果我们的代码不需要任何特别的安全许可,就应该避免使用任何CAS API来判断访问许可,这样做只会导致代码性能的降低。
我们需要使用CAS API来访问一部分需要特别权限的保护性资源,最常见的保护性资源时非托管内存和文件系统,其他保护性资源包括数据库、网络端口号、Windows注册表和打印子系统。在每种情况下,当调用不具适当权限的代码来试图访问这些资源时,都会发生异常。而且访问这些资源可能会导致运行时对堆栈进行一次安全遍历,从而确保在目前的调用栈上,所有的程序集都有适当的权限。
我们可以通过创建可验证安全的程序集,来避免非托管内存访问。安全的策划那个徐机指的是程序集中没有任何指针来访问托管或者非托管堆。
使用非安全代码的理由非常少,最常见的理由是性能。当我们创建非安全代码块时,要考虑将这些算法分离到一个单独的程序集中,这会限制非安全代码对我们整个应用程序的影响,如果实施了分离,那么只有需要特定功能的调用者才会受影响,我们仍然可以在安全限制更加严格的环境中使用其他的安全功能。
对于内存访问的建议非常简单,尽可能的避免访问非托管内存。
对于文件系统,.NET使用隔离存储来解决这个问题,隔离存储可以被看做一个基于程序集、应用程序域和当前用户来划分的虚拟目录。部分受信的程序集可以访问与它们相关联的格里存储区,但不能访问文件系统的其他部分,其他程序集和用户也看不到不属于自己的隔离存储目录。
我们可以使用隔离存储来对一定大小的数据元素进行持久化处理,从而允许部分受信代码从本地磁盘的某个经过精心划分的位置上保存和加载信息。.NET环境为每个应用程序定义了隔离存储大小的限制,这可以放置恶意代码过度使用磁盘空间,以免将系统置于一个不可用的状态。
对于其他需要访问的受保护的资源,在访问这些资源时,都需要我们的程序处于完全受信的状态,否则,我们就只能选择完全避免访问这些资源。
.NET安全模型意味着程序的行为会和它所具有的权限相匹配,我们需要关注程序所需要的权限,并尽量最小化它们,应该避免要求程序本来不需要的权限。程序集需要的受保护资源越少,它产生安全异常的机会就越小,我们应该尽力避免使用需要安全许可的资源。当某些算法确实需要更高的安全许可时,我们应该将那些代码隔离在一个单独的程序集中。