Why is this an issue?
To establish a SSL/TLS connection not vulnerable to man-in-the-middle attacks, it’s essential to make sure the server presents the right
certificate.
The certificate’s hostname-specific data should match the server hostname.
It’s not recommended to re-invent the wheel by implementing custom hostname verification.
TLS/SSL libraries provide built-in hostname verification functions that should be used.
This rule raises an issue when:
-
HostnameVerifier.verify()
method always returns true
- a JavaMail’s
javax.mail.Session
is created with a Properties
object having no
mail.smtp.ssl.checkserveridentity
or mail.smtps.ssl.checkserveridentity
not configured to true
- a Apache Common Emails’s
org.apache.commons.mail.SimpleEmail
is used with setSSLOnConnect(true)
or
setStartTLSEnabled(true)
or setStartTLSRequired(true)
without a call to setSSLCheckServerIdentity(true)
Noncompliant code example
URL url = new URL("https://example.org/");
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
urlConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String requestedHost, SSLSession remoteServerSession) {
return true; // Noncompliant
}
});
InputStream in = urlConnection.getInputStream();
SimpleEmail example:
Email email = new SimpleEmail();
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setSSLOnConnect(true); // Noncompliant; setSSLCheckServerIdentity(true) should also be called before sending the email
email.send();
JavaMail’s example:
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); // Noncompliant; Session is created without having "mail.smtp.ssl.checkserveridentity" set to true
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
Session session = Session.getDefaultInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username@gmail.com", "password");
}
});
Compliant solution
URL url = new URL("https://example.org/");
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
// Compliant; Use the default HostnameVerifier
InputStream in = urlConnection.getInputStream();
SimpleEmail example:
Email email = new SimpleEmail();
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator(username, password));
email.setSSLOnConnect(true);
email.setSSLCheckServerIdentity(true); // Compliant
email.send();
JavaMail’s example:
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.ssl.checkserveridentity", true); // Compliant
Session session = Session.getDefaultInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username@gmail.com", "password");
}
});
Resources