We have an application written in Java. I am concerned about security. I know there are well-known vulnerabilities...
for C++ as far as hazardous functions for buffer overflows. I was wondering if there is a list of functions to look out for in the Java world and any advice you could give on security testing for Java applications.
Unlike C++, Java is designed to provide several inherent security features from ground-up and at all levels from the Java language constructs to the Java runtime environment and from the class libraries to the complete application. I would suggest you first to take a look at the fundamental Java security features within the Java programming language. These give the programmer the advantage and freedom to write secure code and execute and distribute it over a network. Those key Java language security features are as follows:
- The Java language defines all primitives with a specific size and all operations are defined to be in a specific order of execution. Thus, the code executed in different JVMs will not differ from the specified order of execution.
- The Java language provides access-control functionality on variables and methods in the object by defining name space management for type and procedure names. This secures the program by restricting access to its critical objects from untrusted code. For example, access is restricted by qualifying the type members as public, protected, private, package, etc.
- The Java language does not allow defining or dereferencing pointers, which means that programmers cannot forge a pointer to the memory or create code defining offset points to memory. All references to methods and instance variables in the class file are done via symbolic names. The elimination of pointers helps to prevent malicious programs like computer viruses and misuse of pointers such as accessing private methods directly by using a pointer starting from the object's pointer, or running off the end of an array.
- The Java object encapsulation supports "programming by contract," which allows the reuse of code that has already been tested.
- The Java language is a strongly typed language. During compile time, the Java compiler does extensive type checking for type mismatches. This mechanism guarantees that the runtime data type variables are compatible and consistent with the compile time information.
- The language allows declaring classes or methods as final. Any classes or methods that are declared as final cannot be overridden. This helps to protect the code from malicious attacks such as creating a subclass and substituting it for the original class and override methods.
- The Java Garbage Collection mechanism contributes to secure Java programs by providing a transparent storage allocation and recovering unused memory instead of deallocating the memory using manual intervention. This ensures program integrity during execution and prevents programmatic access to accidental and incorrect freeing of memory resulting in a JVM crash.
With the above core security features at the programming language level, Java promises a secure programming environment advantage over using C++. Having said that, I would also like to highlight a few things that will enhance the security of your Java code.
1. Restrict access to your Java classes, methods and objects by declaring it as private and by enforcing a Java policy defining access control privileges and permissions. The Java classloader adopts a security manager and access controller that enforces the required security policy of an application by performing runtime checks and authorizing access, thereby protecting resources from malicious operations.
2. Always sign your code so that it allows identification the signer of the code and has its integrity guaranteed by a certificate authority (CA) -- and additionally it can be trusted to run with the permissions granted in the policy file. Signing the code helps to identify untrusted malicious code and helps the end user allow downloading and executing applications after verifying the signer's information. To facilitate signed applications, JDK 1.1.x added support for cryptographic algorithms that provide digital signature capabilities. With this support, a Java class could be signed with digital signatures in the Java archive format (JAR file). The JDK runtime will use the trusted public keys to verify the signers of the downloaded applet and then treat it as a trusted local application, granting access to its resources. To support function related to digital signature, that Java platform does provide a set of tools that helps create keys (for testing purposes only), manage keys and certificates, sign JAR files, verify signatures, and support other functions related to key management.
3. The threat of reverse engineering is a well-known security problem in Java applications. By default, the byte code generated by the Java compiler contains much symbolic information, including the actual Java source of the executable and debugging information. Using reverse engineering mechanisms, it is possible to disassemble and decompile the executable Java byte code into actual Java source code. To counter these issues, there are several tools and techniques that protect the source code from prying eyes by making it difficult to apply any reverse engineering mechanisms. The possible ways to prevent reverse engineering of Java executables and to protect the source code are as follows:
- Code authentication: This approach adopts evaluation and verification of executable code for trusted sources, runtime checks, predictable behavior, and output. This ensures that code is not executed from an unauthorized host, not modified to produce any unpredicted output, and not duplicated to act as an original application.
- Encryption and decryption: Using encryption and decryption of executable code in transmission ensures that the code is not accessible or tampered with during its transit. This approach limits portability of the application but works well in scenarios where applications are made available via server-side invocations.
- Code obfuscation: This approach uses a transformation mechanism that changes the program and generates Java code with obscure references. The obfuscated code is understood by compilers but difficult to read by humans. This is the most popular way to prevent the success of reverse engineering capabilities on executable code.
References: Core Security Patterns: Best Practices and Strategies for J2EE(TM), Web Services, and Identity Management (Prentice Hall, 2006)
- Core Security Patterns: Best Practices and Strategies for J2EE, Web Services, and Identity Management -- Chapter 8
- Demystifying Java security, Part 1
- How to implement security in Java EE and Java ME