• No results found

The primary goal of this paper has been to identify secure coding practices and constructs that remediate application level vulnerabilities which could, if went unmitigated, result in realization of a variety of threats on enterprise web-based applications. As a way of conducting that discussion, we specifically talked about J2EE and .NET platforms for enterprise web application development. While some issues discussed where specific to these frameworks, most of the application security concepts which we have covered actually have a broad application that would transcend beyond these particular

technologies. For instance, principles of compartmentalization and least privilege are not tied to any particular implementation.

We have seen the various compensating controls in J2EE and .NET that support the core security services and ensure that they are not compromised by the various threats. We have also discussed how developers need to use various security features that are

provided by J2EE and .NET appropriately and looked at certain secure coding guidelines required in order to avoid potentially costly pitfalls. Throughout chapters three and four we pointed out some of the differences and similarities between J2EE and .NET security models and APIs that are available to programmers. We summarize some of this

discussion in this section, highlight the key similarities and differences between these two frameworks, and illuminate on some key strengths and weaknesses of each. The

intention here is not to suggest to the reader that one framework may be more secure than the other, but rather point out the unique security features of each that are important to understand when building secure enterprise web-based applications.

6.1 J2EE vs. .NET Security

We have discussed the various security features of both J2EE and .NET security models and how they help protect the core security services. Additionally, we covered some of

the specific secure programming practices that developers of enterprise web-based applications should follow in order to promote security. For the most part, the programming guidelines have been similar, but the details were different. For instance, we discussed that good input validation against a white list of allowed input is the key to success. However, the actual mechanisms by which programmers can provide input validation in J2EE and .NET differ slightly (although similar). We now compare and contrast some of the security features of J2EE and .NET that support core security services. Some of the points we discuss are: Java Virtual Machine (JVM) vs. CLR (Common Language Runtime) security, J2EE class loader security vs. .NET code access security, J2EE vs. .NET security policy, cryptography. We also discuss role-based security in both J2EE and .NET. Special attention is also given to programmatic and declarative security.

6.1.1 JVM vs. CLR

JVM is responsible for executing an intermediate representation of the Java program classes (i.e. bytecode) on the machine on which it is resides. JVM also plays an essential security role in Java and consequently J2EE security. For instance, “the JVM provides a secure runtime environment by managing memory, providing isolation between

executing components in different namespaces, array bounds checking, etc.” [29]. JVM memory management and array bounds checking protects Java against the single gravest vulnerability category that plagues C and C++ applications, that is buffer overflows. This greatly supports authorization, confidentiality, integrity, and other security services. Since JVM allocates the various memory areas dynamically, it is very difficult for an attacker to figure out where to insert malicious code in a way that would change the flow of program execution. Beyond that, bounds checking on arrays, by preventing

unreferenced memory accesses, leave an attacker looking for other, far more complex and rare, ways to inject malicious code. Additionally, memory management (such as

garbage collection facilities) improve functional system stability and also protect J2EE applications against a wide variety of problems related to poor memory management, including availability issues that may otherwise lead to denial of service attacks. JVM also provides isolation between executing component, enforcing the principle of compartmentalization.

Additionally, JVM contains a class file verifier that examines classes for basic class file structure when loading. This protection mechanism is necessary to ensure that any malicious bytecode inserted by an attacker is detected. Several passes are used by JVM to inspect the bytecode, first for physical attributes (size, some magic number and length of attributes), then for attribute correctness in field and method references, and then for valid instruction parsing for each method.

The CLR in .NET serves a very similar security purpose to JVM in J2EE. CLR provides a secure execution environment through managed code and code access security.

Analogous to JVM, the CLR is used to run the intermediate representation (e.g. intermediate language (IL)) of the .NET program code. CLR provides type checking facilities through Common Type Specification (CTS) that essentially make it impossible

for buffer overflows to occur in managed code. Consequently, both J2EE and .NET runtime environments provide defense in depth against buffer overflow problems. Code access security determines the level of trust assigned to a particular piece of code and it allows for fine grained access control. There is similar support for this in J2EE through the class loader architecture [29].

Both J2EE and .NET may get into serious trouble when it comes to security when outside code needs to be called. For instance, in .NET, if a piece of code needs to be called that is not managed code (e.g. code in a unsupported programming language) then this code will not be able to take advantage of any of the security features of the CLR and thus it will be more difficult to keep it secure from things like buffer overflows and other problems. Analogously, if J2EE needs to work with code written in a language like C or C++, it will be open to similar problems since non of the JVM security features will be useful. Additionally, it will not be possible to apply finer grained access control to outside code, and it will always have to be treated as untrusted.

6.1.2 J2EE Class Loader vs. .NET Code Access Security

J2EE class loader architecture helps provide some of the fine grained authorization capabilities that allow execution of partially trusted code. There are two types of class loaders in J2EE: primordial class loader and object class loader. Primordial class loader is part of the JVM and it bootstraps the JVM by loading the base classes. On the other hand, object class loader is used to load the classes requested by the application. Without going into the specifics here, it suffices to say that primordial class loader in the JVM ensures that all classes loaded by the object class loader are properly authorized and that untrusted code cannot be made to replace the trusted code (base classes). “The loading of class loaders describes a tree structure with primordial class loader at the root and classes as the leaf nodes” [29]. Class loaders prevent class spoofing by passing requests back up the tree, through the parent class loader, until the class loader that loaded the requested class is reached. This is similar to tracing a certificate used in PKI to a trusted CA. Class loaders support additional security through managing the security namespaces, where a particular class loader may only reference other classes that are part of the same

namespace (i.e. loaded by the same class loader or its parents).

Code access security is enabled in .NET through usage of evidence-based security and permissions. While we have already discussed both of these in far more detail in chapter four of the paper, we briefly touch on them here for the sake of discussion. Analogously to JVM, the CLR inflicts certain checks on .NET assemblies to ascertain to what degree they can be trusted and what code they can access. Evidence-based security uses two main criteria in the assembly querying: where did the code originate and who created the assembly. Digital certificate signed by the assembly publisher or a strong assembly name comprised of the unique identifier, text name, digital signature and a public key, can serve as evidence-based security tokens. For the purpose of determining who created the assembly, assembly’s metadata is used that includes information on types, relationships with other assemblies, security permissions requested and assembly description. Metadata is examined by the CLR at various stages. The verifier module in the CLR

ensures that IL types are correct before compiling IL to the native code and that assembly metadata is valid. The CLR further examines the metadata to establish the identity of the assembly and the permissions granted to the assembly based on the security policy [29]. Permissions inside the CLR define the rights of a piece of code to access a particular resource or perform a particular operation. An assembly will not be given greater permission than what is allowed by the security policy, but may be given lesser permission (in line with the least privilege principle). The assembly requests certain permissions at runtime, which are either granted or denied by the CLR. The CLR performs a “stack walk” to determine whether the given assembly has the necessary permissions to be granted access to the requested resource.

6.1.3 J2EE vs. .NET Security Policy

The basis for code access permissions, in both J2EE and .NET is the underlying security policy. In J2EE, the access controller makes the permissions decisions based on the security policy. The CLR resolves permission decisions by querying the access controller about the security policy as it relates to the particular permission of the assembly being evaluated. As we previously discussed, according to J2EE security policy, permission sets are grouped in J2EE into protection domains associated with the various code sources. Groups of permissions are associated with groups of classes and classes are grouped by origin [29]. Signed Java code is assigned permissions in

accordance with the system policies as applied to the protection domain from which the code originates. It should be noted that the default configuration is not necessarily

secure, since by default, Java applications are not associated with any permission domain, and thus have full access to system resources.

Analogously, .NET security policy is managed by the Code Access Security Policy Tool. The CLR uses the information in the security policy to determine at run-time the

permissions that the assembly has. This is done after the assembly is identified through evidence-based security techniques that we have described. Similarly to J2EE protection domains that are assigned certain permissions, the security policy in .NET groups the categories based on the assembly evidence information, such as the zone from which the code is loaded. There are certain permissions associated with each of the zones,

however, individual assemblies may have finer grained permissions. In this way, .NET security zones allow for more flexibility than J2EE protection domains, where all of the components in the protection domain have the same permissions.

6.1.4 Role-Based Security

In addition to code access security, both J2EE and .NET provide support for role-based security, where access control decisions to resources and operations are made based on the role of the individual on whose behalf the permission is requested. .NET defines various membership roles for the application. .NET uses role-based security for authenticating and authorizing individual users. When a user is identified, his

Principals can be members of one or more roles (e.g. John Smith, CFO, Executive). The access control decision is similar to that for code access security, with the exception that the permission structure now depends on and is managed through the PrincipalPermission object [29]. J2EE also supports role-based security through a concept of a principal (an authenticated and authorized identity) that has certain permissions. In J2EE, the

principals are often specified declaratively (via container’s deployment descriptors) and may be accessed programmatically in order to check for permissions.

6.1.5 Programmatic and Declarative Security

Both J2EE and .NET support programmatic security, where developers can program security checks in the code. This makes it critical for developers to be aware of the proper ways to make use of the various security APIs and features provided by these platforms. Developers be may make use of code access security, role-based security, cryptography functionality, and other security related functionality in a programmatic manner. J2EE also makes extensive use of declarative security that is specified via XML deployment descriptors and is handled by the container. The deployment descriptors may be specified at deployment time. Additionally, declarative security places fewer burdens on the programmer, thus reducing the risk of insecure

programming. For instances, security roles and permissions can be specified through deployment descriptors. .NET does not provide an equivalent mechanism for declarative security.

6.1.6 Cryptography

Both J2EE and .NET provide adequate cryptographic functionality, that when used properly, will support the core security services, including confidentiality, integrity, etc. .NET base classes provide support for encryption, key generation and HMAC through a variety of accepted cryptographic algorithms. .NET framework also provides tools for certificate management and manipulation. For instance, X.509 certificates can be created and managed. We have discussed some of the cryptographic APIs available in J2EE including JCE and JAAS. J2EE also contains APIs that contain SSL and TLS functionality that developers may use.