Applications have continued to evolve from desktop to enterprise, the cloud, and laterally into the Internet of Things and embedded devices. Each evolution increases business benefit and, conversely, creates more opportunity for successful exploitation. Further, traditional security infrastructure like firewalls are proving less effective at defending applications. Few companies have a handle on their Java platform security and the seams are starting to show.
According to the 2014 Application Vulnerability Trends Report by Cenzic, 96% of the web applications they tested had vulnerabilities. For example, in November 2015, Foxglove Security presented research demonstrating how deserializing objects from untrusted sources creates a security concern. The report raised concern for popular services like WebSphere, JBoss, Jenkins, and WebLogic. No concrete examples of attacks were provided, but the event stirred vendors to make improvements within their products.
Understanding Java platform security (specifically, session state management) is essential no matter what product or service you’re building. We’ve spelled out the basics along with some of the most important precautions to take when designing your application. Let’s take a look:
Getting session management right
HTTP is a stateless (non-persistent) protocol, meaning each request is treated on its own. The structure was purposely created this way for simplicity, yet raises an issue for many web application systems. Necessary tasks such as keeping track of items in an online shopping cart or tracking user trends become difficult. Correlating successive user HTTP requests is then left up to the designers of application infrastructure.
If you’re writing a J2EE system, it’s likely that most of this is handled by your application server. On occasion, applications may be defined differently than the scope of a web application server, forcing developers to create their own infrastructure. If you find yourself with a requirement to design your own session management for your application, we recommend avoiding some of these common pitfalls:
- Protect session IDs. Once the bad guys exfiltrate a session ID, they are able to hijack authenticated sessions from users – that’s bad news. To combat this you can use transport encryption like HTTPS/TLS to protect session and user data from disclosure.
- Make session IDs impossible to guess. A session ID that is easy to guess allows attackers to hijack sessions with little effort. To avoid this, you should generate session IDS that employ cryptographic algorithms to deter reverse engineering. Also, make sure to protect both key and salt values, but keep in mind, hardcoding these values is strongly discouraged.
- Adopt industry techniques. The devil is in the details with security. It’s easy to forget a minor detail that could be used to exploit your system, such as including a session ID in the URL. Since URLs often show up in logs that are mined by attackers, mistakes have a profound impact on application security posture. When practical, adopt industry standard techniques.
C is for cookie
Cookies are a reliable, industry standard practice for persisting client data. They provide a number of advantages when used for session IDs, but be sure to consider these four security features when defending your application:
- Set Secure attribute. The Secure cookie attribute instructs web browsers that the cookie is only to be sent over secure connections. Including the flag increases the cost for attackers to exfiltrate using man-in-the-middle (MitM) attacks or similar forms of attack. Make your cookie secure by calling Cookie.setSecure.
- Set Domain and Path. Domain controls the scope of distribution within the target domain and its subdomains. It’s recommended to set the narrowest practical scope, reducing the risk of exfiltration. Similarly, reducing path scope reduces risk. Set domain and path scope with values appropriate for your application like this: Cookie.setDomain(“.acme.com”) and Cookie.setPath(“/”).
- Set Expire and Max-Age attributes. The Expire attribute time stamp causes a cookie to be deleted once reached. Max-Age causes the cookie to be removed once a specified number of seconds has elapsed. If both are present, Max-Age has precedence over Expire. Set Expire and Max-Age like this: Cookie.setMaxAge(N) where N is number of seconds. Because the web browser manages all attributes, whether or not a cookie is deleted is entirely a browser responsibility.
Let’s talk about trust
Why is trust so important? Keep in mind these few tips to improve Java Platform security.
- Don’t trust data from web browsers. Web applications cannot trust that the cookies received by browsers are the same cookies that were written previously by the application server. To keep data free from tampering, you will need to keep the data on the server in the client’s HttpSession or encrypt the cookie data.
- Session entropy. Session IDs must be cryptographically secure to defend from tampering. Remember that attackers can easily hijack a session by bypassing the application login screen.
- Renew the session after privilege level changes. You will want to consider invalidating the session using HttpSession.invalidate() if the user’s security privileges change. A common issue is that privileged information is stored in cached user sessions. Forcing a new logon releases any previous cache data that may be associated with the user’s old HttpSession instance.
Serializing the session
Imagine you’re designing a stateless server system. A stateless server is efficient to scale since HttpSession instances don’t need to be replicated or made sticky. In such a design, client state data is kept to a minimum and stored as cookies on the client. Another practical example would be an IoT device that sends serialized data back to the server. In these cases, there are some considerations for client-side serialization to remember.
- Authenticate clients. You shouldn’t trust data from any untrusted sources. However, deserializing data from untrusted sources presents a complex security concern, since the data may be difficult to validate until it is deserialized. Authentication is important because it reduces the attack surface to authorized users.
- Encrypt and sign payloads. Another option is to sign and encrypt serialized objects. Encryption should be used where confidentiality must be preserved. If confidentiality is not a concern but tamper-resistance is, then you can cryptographically sign the serialized object. The advantage of both of these methods is that an authorized client can serialize the object, encrypt or sign it, and finally that the payload can be sent over untrusted networks.