5 deadly security sins of a Java developer

Every Java application is exposed to a dangerous world, where some people want to break it. Some of them will do it just for fun, others to steal (money or user data, or anything else), others just to gain free access to features that are supposed to cost money. The reasons may be numerous, and you should never think that you are not going to become their victim until you have 100 million customers and millions in revenue. Even if you’ve just opened an internet store, you’re likely to be vulnerable to such threats. Do you think, your users will love you when their personal data and email addresses will be sold to a spam lists? I doubt so.

So, one should keep security in mind from the very beginning when designing even a small Java app. Of course, most of the below principles and typical design mistakes are applicable to any app, not only Java.

These are the most common sins of Java developers, and hackers will always start with exploring them.

1. Input validation

This is the most common mistake when the developer is not validating user input. Remember: any input, including user input, API calls, external databases, 3rd-party products must always be validated on server-side!

You think that AngularJS or some other fancy web framework you are using is already doing this for you? But it’s super easy to record the traffic between the Web page and the server, and then replace any symbols in Ajax calls to whatever the hacker wants. You don’t even need to know how to write code for that, every modern browser has this standard functionality in dev mode. So, input validation in the GUI is for user-friendliness, input validation on server-side is for security.

It is also very important how exactly the validation is performed. First of all, check character encoding. If you expect it to be UFT-8 – then just drop all other symbols. If there is specific character set that is supposed to be present – always check that there are only expected symbols. Never just check for escapes characters and special symbols (for example, % is the most commonly used for SQL injection). Instead, validate against allowed character set. For example, if you expect alphanumeric symbols – strictly check the input using regex \p{Alnum}.

Always check input length and data range for dates and numbers.

If the request fails the input validation – reject it completely.

2. Authentication

Remember: authentication must always be handled by the server. If you have authentication on client side – you don’t have it at all.

There are endless articles on password length, strength and cryptographic security of storing password hashes, so we’ll skip this part.

What is important is: are you sure you want to handle it yourself? There are several providers and 99,99% of the users already have accounts with some of them, for example, Google and Facebook. Integrating with their authentication is easier than it looks, and it significantly improves conversion ration for new users.

Another common topic worth mentioning is two-factor authentication. It is not always required to do this via SMS (which definitely would be an overkill for a new application with handful of users), but services like Google Authenticator make it easier to adopt even for new players.

3. Cross-site scripting

Cross-site scripting (also known as XSS) is one of the most common attacks when a malicious script is injected in a trusted web application. For example, if a user visited malicious web site in the same browser as the trusted site, harmful JavaScript code may be sent to the server and the server may return it back to the browser. In this case, the browser will execute that script, because it looks like a normal legitimate JavaScript, and that script may do anything, from stealing session data to replacing any part of the trusted web page with malicious content. For example, such script may display authentication window and then send user name and password to malicious web site.

To protect your application against this type of attack, the following techniques should be used.

Never display user input back to the user before it was validated and properly escaped. It also means, if you are rejecting user input due to validation error, never display what the user entered, just display general error message like “Date validation failed”, but never display the date that didn’t pass the validation.

Always validate and escape user input before storing it in the database. We recommend using some standard escape library, for example, from ‪owasp.org.

Always set X-FRAME-OPTIONS=deny in HTTP header for all requests. This will not only help prevent this attack, but also clickjacking type of attack. Clickjacking means that a malicious site will include your site in an iframe, and in this case it may intercept mouse clicks.

4. Session hijacking

This scenario is a bit complicated, but is also often used. Imagine that a user has a session established with your site, and simultaneously she has malicious site open in another browser tab. That malicious site may host a JavaScript that will sent a POST or GET request to your site, with some dangerous action, for example, payment or password change. The browser will automatically include existing session cookie into the request, because the user has already authenticated in another tab. In this case, the requested action will be performed with full user permissions, like if the user actually did it herself.

In order to prevent this type of attack, the following technique may be used. When the HTML page first tries to log user in, it will generate random string, which is called XSRF token, and send it to the server, together with the login request. The server will create a session and associate it with XSRF token. HTML page will then send it with every subsequent request to the server, and the server will always check that this is the same token that was stored with the session id. This XSRF token is not accessible through other browser tabs, and thus session highjacking attack will be prevented.

5. Mind your header.

This is the last and the most simple step. By default, server will send the exact engine version in the header. For example, Server: Apache-Coyote/1.1. So, the hacker will already know which HTTP or application server you are using, and will start attacking known vulnerabilities. It’s a good idea to send fake information in this field. Not that the bad guys will not be able to find it out on their own, but why make their life easier, right?

Next goes the name of the cookie that is used for session management. By default, it’s JSESSIONID in many servers. It takes only a few seconds to change it to something unique for you. For example, for Weblogic, edit your application’s webogic.xml and add <cookie-name>YOURSUPERCOOKIE</cookie-name> to <session-descriptor>. It also makes sense to add <cookie-secure>true</cookie-secure> if it’s not already there.

Overall, remember, there is no absolute protection. Every application may be broken into, and every information may be stolen. But the more difficult it is to break into your system (and hence more expensive) – the more expensive and sensitive information you must keep there in order to justify the cost. So, even few simple steps that require probably 10 additional minutes each of the hacker’s time may make breaking into your system absolutely worthless for the bad guys.

Implementing HTTPS client in Java – part 1

HTTP had become de-facto standard for interprocess communications in all systems where real-time behavior is not a requirement. It’s very convenient in heterogenous environments where more than one programming language is in use or where formal communication contract should be established.
When it comes to the response side, there are a lot of standard servers and frameworks that make it very easy to create, test, deploy, configure and monitor the server-side HTTP code, including Apache Tomcat, Jetty and many more. But when it comes to the client-side of HTTP, it usually requires some more development effort, though it should have been vice versa.
So, we will create an example of a client Java code with support for two-way SSL encryption. In this article, we will only focus on transport layer, i.e. the actual request payloads may be XML, JSON, plain text or whatever else. Suppose, that the server part of the system is already running in some Web server and you know the host, port, application context root and have a certificate to check the server signature. We’ll only touch the client part.
I will make myself some tea (you are encouraged to make coffee if you’re not a tea fan), and let’s start with some required imports and create our class.
1
2
3
4
5
6
7
8
9
10
11
12
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.ServletConfig;

public class OurSSLClient implements X509TrustManager, HostnameVerifier {     Map<X509Certificate, Long> trusted = new HashMap();

Now we have our class, and you probably noticed that HashMap that we declared. This is because we’re not going to verify our server on every call (this is time and resource consuming, and even the best tea may not justify such waste of time). So, when we check the certificate presented, we’re going to store it in the HashMap, with the timestamp of the last verification. In this way we can easily check after configurable timeout if we want.
Also, you should have noticed that our class implements two interfaces, so there are several important methods which we should implement. Let’s do this boring job later, and now focus on interesting part – open connection and read response.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  String getData(String url, String request) {
   int responseCode = 0;
   HttpURLConnection connection;
   try {
      URL httpURL = new URL(url);
      connection = (HttpURLConnection) httpURL.openConnection();
      //Https- specific setup
      connection.setRequestMethod("POST");
      connection.setDoOutput(true);
      connection.setRequestProperty("Content-Length", Integer.toString(request.length()));
      connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
      connection.setDoOutput(true);
      DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
      wr.writeBytes(json);
      wr.flush();
      wr.close();
      responseCode = connection.getResponseCode();
      String response = null;
      BufferedReader rd = null;
      InputStream is = null;
      if (responseCode == 200) {
         is = connection.getInputStream();
      } else {
         is = connection.getErrorStream();
      }
      rd = new BufferedReader(new InputStreamReader(is));
      StringBuilder sb = new StringBuilder();
      String line;
      while ((line = rd.readLine()) != null) {
         sb.append(line);
      }
      response = sb.toString();
      return response;
   } catch (IOException e) {
      e.printStackTrace();
      return null;
   } finally {
      connection.disconnect();
   }
}
This beautiful code actually opens connection to our server, sends request using HTTP POST method, and then reads response. If your server is pure HTTP, you don’t actually need to do anything else!
But you may be curious enough to ask where is the promised HTTPS. Here is goes! Now replace comment “HTTPS specific setup” with the below code:
            
if (serverURL.startsWith("https")) {
   HttpsURLConnection con = (HttpsURLConnection) connection;
   SSLContext SSL_CONTEXT = SSLContext.getInstance("TLSv1.2");
   KeyManager[] km = getKeyManager();
   SSL_CONTEXT.init(km, new TrustManager[]{this}, new java.security.SecureRandom());
   con.setSSLSocketFactory(SSL_CONTEXT.getSocketFactory());
   con.setHostnameVerifier((HostnameVerifier) this);
}

This code will do the trick but turning our HTTP connection into HTTPS. The only missing thing here is the getKeyManager() method, which we need to implement in order to provide the certificate, so that your client may identify yourself for the server. The implementation normally depends on the PKI infrastructure that is set up in your organization, but if you don’t have any, the most basic implementation will require a JKS storage file and two passwords: keystore password to access the keystore, and a password for the private key that you will use to encrypt and sign the data you are sending to the server, so that the server may verify your identity.

If you experience any difficulties creating keystore and generating private and public keys, please let us know, and we will post an article on this topic.
     
public KeyManager[] getKeyManager(String filePath, String password, String keyPassword) {
   KeyStore ks = null;
   try {
      char[] storepass = password.toCharArray();
      char[] keypass = keyPassword.toCharArray();
      if (ks == null) {
         FileInputStream fin = new FileInputStream(KEY_STORE_PASS);
         ks = KeyStore.getInstance("JKS");
         ks.load(fin, storepass);
      }
      KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 
      kmf.init(ks, keypass); 
      return kmf.getKeyManagers(); 
   } catch (Exception e) { 
      e.printStackTrace(); 
   } return null; 
}
Almost done! Now we will need to implement couple of methods for two-way SSL. First, let’s check that the server we are connecting to actually identifies itself with the certificate given to the correct host:
@Override
public boolean verify(String string, SSLSession ssls) {
   if (string == null) {
      log.error("hostname is null");
      return false;
   }
   return string.equalsIgnoreCase(ssls.getPeerHost());
}
Next very important method is checkServerTrusted. In almost all manuals in the internet this method just returns true. As you might guess, it’s completely wrong. Instead, it should check the validity of the server certificate including check in the CRL (Certificate Revocation List) that the certificate was not revoked. CRL check is really tricky topic that requires a lot of lines of code, so we will do this in one of the next posts. For now, let’s just return true and save the certificate presented by the server:
    
@Override
public void checkServerTrusted(X509Certificate[] arg0, String crypto) throws CertificateException {
   if (arg0 != null) {
      for (X509Certificate item : arg0) {
         if(!trusted.containsKey(item) || (System.currentTimeMillis() &gt; (trusted.get(item) +3600*1000*24))) {
             // Will do the actual check later
            trusted.put(item, System.currentTimeMillis());
         }
      }
   }
}

And the last couple of methods:

    
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
   X509Certificate[] res =newX509Certificate[trusted.size()];
   trusted.keySet().toArray(res);
   return res;
}
That’s it! Now we have successfully configured HTTPS connection with two-way SSL, and you may start programming real application logic.

JavaSourceCode updated

Welcome to the all-new JavaSourceCode.org. For many years this site was great place for experienced Java developers to meet and contribute to the open source project. And many of JavaSourceCode.org authors contributed to the most successful Open Source projects including Apache Cassandra, Eclipse, Hadoop, Hibernate, Hive and many others. And their contribution was not just development of those projects but also implementation of the Open Source technology in the largest setups in the industry.