The .NET Framework is a flexible general-purpose computing platform designed to address the needs of commercial organizations and individuals alike, and supports many different application models. However, .NET places an emphasis on supporting the trend towards highly distributed systems, component-based applications, and web-based server solutions, including XML web services.
The .NET Framework is designed to run on multiple operating system platforms. Although Windows is currently the platform of choice for .NET deployment, there are implementations available for FreeBSD, Linux, and Mac OS X that are compliant with ECMA Standard 335 (which defines the core functionality of the .NET Framework). It is likely that in the future, .NET applications compliant with ECMA Standard 335 will run and integrate seamlessly across a variety of other operating systems.
The .NET Framework's strong support for distributed application models and its cross-platform capabilities means that software is more mobile, accessible, and integrated than before; although this provides functionality and productivity benefits, it also means that software consumers, producers, and service providers need to place a greater emphasis on software and system security. The increase in accessible web-based server solutions and mobile components also produces an increase in the number of people who try to hack those systems or who distribute malicious code in the form of viruses, worms, and Trojan horses for the purpose of gathering information or causing damage.
Cross-platform solutions cannot always rely on the underlying operating system to provide the same security services on all platforms. The .NET runtime implements the following three features that provide platform-independent security services to improve the security of both client- and server-side software solutions:
These features surround and permeate the .NET runtime to ensure that managed code does not perform actions and access resources it should not. They provide a baseline set of security, enforced by the .NET runtime, that managed code must comply with and can depend on regardless of the underlying operating system. These features do not replace existing operating system security measures; they either augment or compliment existing platform security, and mean that the .NET Framework provides one of the most secure and flexible environments in which to run distributed software solutions.
Runtime security works because the CLR is a managed execution environment that goes to great lengths to ensure that the code it loads is type-safe, meaning that it does not perform illegal operations, access memory directly, or try to access type members incorrectly. We described the validation and verification process used to determine type safety in Chapter 4. Code that is not type-safe is referred to as unverifiable code.
During execution, the .NET runtime, class libraries, and all required application assemblies are loaded to the same operating system process, albeit across different application domains. If type safety is not enforced, malicious code could manipulate the state of the runtime's security system, enabling it to access resources and carry out operations to which it would not normally have permission.
Another common risk to runtime security is the use of native code. Clearly, for the purpose of backward compatibility and to enable access to functionality not yet provided by the .NET class library, managed code must be able to call native code, such as Win32 DLLs and COM objects. However, once native code is running, the .NET runtime has no control over the actions the code performs; malicious native code can delete important files or even change the .NET runtime's security configuration files, which are stored as XML. Only the underlying operating system security can control the actions of unmanaged code, and this is not always sufficient because it bases security decisions on the identity of the current user.
Despite the risks, the .NET runtime designers understood that there are situations when it is necessary to use both native and unverifiable code. However, the runtime restricts access to these capabilities using its code-access security mechanism. Permission to load unverifiable code or call native code are two of the highest trust permissions you can grant to code. Once you grant these permissions to code, you are placing the security of your system at risk. You should have the utmost trust in the source and integrity of code before granting it these capabilities and must be confident that your operating system security is sufficient to protect your machine against attack. We discuss the permission to load unverifiable code and to run native code in Chapter 7.