Charteris White Paper:
Security Issues Associated with the .NET GAC and Code Signing Version 1.0 John Povall
[email protected]
28 February 2003
2003 Charteris plc
CONTENTS 1.
INTRODUCTION
3
2.
BACKGROUND
3
3.
THE GLOBAL ASSEMBLY CACHE
3
4.
SECURING SHARED ASSEMBLIES
4
5.
ASSEMBLY EVIDENCE
5
6.
LIMITATIONS OF STRONG NAME TECHNOLOGY
5
7.
RECOMMENDATIONS
7
28 February 2003 Version 1.0
Security Issues Associated with the .NET GAC and Code Signing Charteris White Paper:
Page 2
1.
INTRODUCTION This paper sets out the security merits of using the .NET GAC (Global Assembly Cache) and code signing, and makes high-level recommendations for secure use of the GAC. The broader issues surrounding security of shared .NET assemblies are outside of the scope of this paper. This paper focuses on the current version of the .NET Framework at the time of writing – version 1.0 Service Pack 2. However, the recommendations should still be valid for the forthcoming version 1.1.
2.
BACKGROUND Shared code libraries have traditionally been a cause of unreliability in software running on Microsoft operating systems. Applications were often ‘broken’ because another application installed a different version of an existing DLL (Dynamic Link Library) without fully providing backward compatibility, or installed a new DLL that coincidentally had the same name as an existing library. Before the .NET Framework, shared code libraries were sometimes spoofed, with a malicious attacker substituting an existing DLL with one that undermined security. One of Microsoft’s key objectives for the .NET Framework was to eliminate this cause of unreliability and insecurity. Microsoft’s solution is based on the Global Assembly Cache (GAC) and Strong Naming of assemblies.
3.
THE GLOBAL ASSEMBLY CACHE In the .NET Framework, assemblies that are shared between applications must be stored in the GAC. The simplest way to prevent applications from having unwanted effects on each other (either accidental interference, or due to malicious software) is for an application to keep its assemblies private and not use the GAC. Developers should never make assemblies available to other applications by using the GAC unless it is necessary and the assembly has been sufficiently protected. Microsoft attempted to build in a safeguard against careless use of the GAC by making the default behaviour of assemblies that may be stored in the GAC1 to require calling assemblies to hold Full Trust permissions. This safeguard has some undesirable side effects, which are outside of the scope of this paper. Nonetheless, it is appropriate to deploy some code in shared assemblies in the GAC. The remainder of this paper focuses on the associated security issues.
1
When a Strong Name is created for an assembly (which is a pre-requisite for installation in the GAC), the default behaviour is to only allow callers that have Full Trust permissions. This default behaviour may be over-ridden by use of APTCA (Allow Partially Trusted Callers Attribute).
28 February 2003 Version 1.0
Security Issues Associated with the .NET GAC and Code Signing Charteris White Paper:
Page 3
The core security functionality of the GAC is as follows: ♦ Each assembly in the GAC is identified not only on the basis of an assembly name, but also using a version number, culture (which allows localised versions of assemblies) and a public key token. It is mandatory that each assembly in the GAC have a Strong Name, which involves the assembly being digitally signed by its publisher. ♦ An assembly’s public key token2 is derived from the public key3 associated with the private key that the publisher used to digitally sign the assembly. ♦ Although it is mandatory that assemblies in the GAC have a Strong Name digital signature, it is possible during software development to prevent the .NET Framework from verifying the signature (so called Delay Signing). This saves developers from having to go through the chore of re-signing an assembly each time it is changed during development. The GAC therefore provides the following core security benefits: ♦ Accidental ‘collisions’ between assemblies are prevented since each assembly that calls a shared assembly can specify the required version, culture and public key token. No confusion will exist between assemblies with different version numbers (different versions can exist side by side in the GAC) or assemblies that just coincidentally have the same name (since the public key tokens will be different). ♦ The .NET Common Language Run-time by default tries to bind to the exact version of the assembly requested by the caller4. This can be overridden (e.g. to specify that the newest version of an assembly should be used) using a publisher policy or using configuration files but this may result in unreliability and should be generally avoided. ♦ If an attacker tampers with, or tries to spoof, an assembly in the GAC, the Strong Name digital signature will be invalidated and the assembly will fail.
4.
SECURING SHARED ASSEMBLIES Assemblies that are stored in the GAC are more difficult to secure than private assemblies, and the impact of a security vulnerability is more severe. There are many
2
A public key token takes the form of a SHA1 hash of the public key, truncated down from its full length of 160 bits to the last 64 bits. This is still OK from a security perspective, since the public key token only needs to avoid accidental collisions between assemblies. Even if an attacker was able to find a different public key that generated the same public key token, they would still be unable to circumvent the .NET Code Access Security policy, which uses full public keys, rather than public key tokens.
3
In the current version 1 Strong Name, the key pair uses RSA with a 1024 bit modulus.
4
Specifically, version numbers are represented in four parts: major, minor, build, revision. The .NET CLR by default tries to bind to an assembly in the GAC where all four parts of the version number are as requested by the caller. This is a change from early beta versions of the .NET Framework that had less rigid assembly binding rules.
28 February 2003 Version 1.0
Security Issues Associated with the .NET GAC and Code Signing Charteris White Paper:
Page 4
issues involved in ensuring that a shared assembly cannot be coerced into undermining security and ensuring that its permissions aren’t misused. This topic is a major part of Charteris’ recommended best practice for.NET security guidelines and would involve adoption of a defensive methodology in which developers try to prevent potential interactions between assemblies that carry associated security risks. Important areas include strictly controlling inheritance of classes in an assembly, minimising the permissions granted and used by code and focussing on ‘trust interfaces’, where it is necessary for a more highly trusted assembly to provide services to less trusted assemblies.
5.
ASSEMBLY EVIDENCE Strong Names have a unique role in disambiguating assemblies in the GAC, but also have a general role as a type of assembly evidence in the .NET Framework. Evidence has the following general uses: ♦ Assignment by .NET Code Access Security (CAS) policy of assemblies to a particular Code Group for the purpose of determining the maximum set of permissions that may be granted to an assembly. ♦ Demanding particular evidence, e.g. a class demanding that any class that inherits from it be in an assembly with a particular Strong Name. .NET security policy could require that other evidence types be used for shared assemblies beyond a Strong Name. To understand the circumstances under which this may be appropriate, it is necessary to understand the limitations of the current Strong Name technology, which are considered in the following section.
6.
LIMITATIONS OF STRONG NAME TECHNOLOGY The tool Microsoft provides to generate and manage Strong Names (sn.exe) does not encourage good key management practices. Its default mode of operation is for private keys to be stored in unencrypted files. A Cryptographic Service Provider (CSP) should always be used to manage key pairs for Strong Names. It is also important that the keys are generated within the CSP, rather than being generated externally (e.g. using sn.exe) and then imported. Unless developers are extremely careful when using Microsoft’s provided tools, plaintext private keys will be vulnerable. Preferably, a CSP should be used that generates, stores and protects private keys in a hardware device and does not allow keys to be exported in unencrypted form. Once a key pair has been used to generate Strong Names, there is no mechanism to revoke the keys or to rollover to a new key pair. Consequently the impact should the private key be compromised or lost is severe. Loss of the private key is a particular concern, since the ability to create new versions of an assembly with the same public key token would be lost. It wouldn’t even be possible
28 February 2003 Version 1.0
Security Issues Associated with the .NET GAC and Code Signing Charteris White Paper:
Page 5
to implement minor bug fixes – all applications that are dependent on assemblies signed with the lost key would have to be recompiled. Some of the limitations of Strong Name technology (in the areas of key rollover and the risk of private key compromise) may be overcome by applying an Authenticode digital signature to assemblies, in addition to a Strong Name signature5,6. Authenticode uses industry standard PKI technology and, critically, supports the use of revocation status information, e.g. in the form of a Certificate Revocation List (CRL). However, use of Authenticode would not deliver significant benefit unless infrastructure is put in place to deliver highly current revocation status information to computers using the .NET Framework. Putting this infrastructure in place would involve significant cost and complexity. Use of a dual Strong Name and Authenticode approach would deviate from standard .NET practice and may introduce complications when moving to new versions of the .NET Framework in the future. Use of Authenticode does not address all of the limitations of Strong Name technology. In particular, loss of the private key used for Strong Name signing would still lead to severe disruption. Consequently, on the basis of information currently available, Charteris does not recommend that Authenticode signing be used in addition to Strong Name signing of shared .NET assemblies. Instead, focus should be placed on developing rigorous procedural controls over Strong Name signing.
5
Note that the Strong Name signature is required in addition to the Authenticode signature, since the .NET Framework is reliant on the public key token of the Strong Name public key in order to perform assembly binding.
6
In principle, the same key pair could be used for both Strong Name signing and Authenticode signing of an assembly, resulting in some administrative simplification.
28 February 2003 Version 1.0
Security Issues Associated with the .NET GAC and Code Signing Charteris White Paper:
Page 6
7.
RECOMMENDATIONS Charteris’ general recommendations for use of the GAC within the .NET Framework are that: R1
All .NET assemblies should be given a Strong Name, even assemblies that will be private and not stored in the GAC. In addition to providing cryptographically secure evidence for Code Access Security policy, a Strong Name provides a strong integrity check on assemblies and allows an application to perform an authentication check on private assemblies.
R2
Detailed guidelines should be implemented for the handling of Strong Name key pairs, in order to prevent key loss or compromise, which would have severe consequences.
R3
Although some of the limitations of Strong Names (e.g. lack of support for key revocation and rollover) may be overcome by applying Authenticode digital signatures to assemblies in addition to a Strong Name signature, on balance Charteris do not recommend this approach due to the increased administrative complexity. Instead, procedural measures should be implemented to manage the risks associated with Strong Names.
R4
Developer guidelines for .NET should promote the use of private assemblies, except where it is strictly necessary for an assembly to be shared and installed in the GAC.
R5
Assemblies that are to be stored in the GAC will be subjected to a more hostile security environment than private assemblies, and consequently will require additional security review activities.
R6
Security of the GAC is critically dependent on maintaining the integrity of configuration files stored on the computer on which the .NET Framework is installed, native code that forms part of the Framework and on Framework registry settings. Guidelines for .NET security should specify in detail how each of the resources that form the .NET Framework ‘trusted computing base’ are to be protected.
28 February 2003 Version 1.0
Security Issues Associated with the .NET GAC and Code Signing Charteris White Paper:
Page 7