Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>JavaMail is commonly used in Java applications to send emails. There are popular third-party libraries like Apache Commons Email which are built on JavaMail and facilitate integration. Authenticated mail sessions require user credentials and mail sessions can require SSL/TLS authentication. It is a common security vulnerability that host-specific certificate data is not validated or is incorrectly validated. Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack.</p>
<p>This query checks whether SSL certificate is validated when username/password is sent in authenticator and when SSL is enabled.</p>
<p>The query has code for both plain JavaMail invocation and mailing through Apache SimpleMail to make it more comprehensive.</p>
</overview>

<recommendation>
<p>Validate SSL certificate when sensitive information is sent in email communications.</p>
</recommendation>

<example>
<p>The following two examples show two ways of configuring secure emails through JavaMail or Apache SimpleMail. In the 'BAD' case,
credentials are sent in an SSL session without certificate validation. In the 'GOOD' case, the certificate is validated.</p>
<sample src="JavaMail.java" />
<sample src="SimpleMail.java" />
</example>

<references>
<li>
<a href="https://cwe.mitre.org/data/definitions/297.html">CWE-297</a>
<a href="https://issues.apache.org/jira/browse/LOG4J2-2819">Add support for specifying an SSL configuration for SmtpAppender (CVE-2020-9488)</a>
<a href="https://rules.sonarsource.com/java/tag/owasp/RSPEC-4499">SMTP SSL connection should check server identity</a>
</li>
</references>
</qhelp>
103 changes: 103 additions & 0 deletions java/ql/src/experimental/Security/CWE/CWE-297/InsecureJavaMail.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @id java/insecure-smtp-ssl
* @name Insecure JavaMail SSL Configuration
* @description Java application configured to use authenticated mail session over SSL does not validate the SSL certificate to properly ensure that it is actually associated with that host.
* @kind problem
* @tags security
* external/cwe-297
*/

import java

/**
* The method to set Java properties
*/
class SetPropertyMethod extends Method {
SetPropertyMethod() {
this.hasName("setProperty") and
this.getDeclaringType().hasQualifiedName("java.util", "Properties")
or
this.hasName("put") and
this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.util", "Dictionary")
}
}

/**
* The insecure way to set Java properties in mail sessions.
* 1. Set the mail.smtp.auth property to provide the SMTP Transport with a username and password when connecting to the SMTP server or
* set the mail.smtp.ssl.socketFactory/mail.smtp.ssl.socketFactory.class property to create an SMTP SSL socket.
* 2. No mail.smtp.ssl.checkserveridentity property is enabled.
*/
predicate isInsecureMailPropertyConfig(VarAccess propertiesVarAccess) {
exists(MethodAccess ma |
ma.getMethod() instanceof SetPropertyMethod and
ma.getQualifier() = propertiesVarAccess.getVariable().getAnAccess() and
(
getStringValue(ma.getArgument(0)).matches("%.auth%") and //mail.smtp.auth
getStringValue(ma.getArgument(1)) = "true"
or
getStringValue(ma.getArgument(0)).matches("%.socketFactory%") //mail.smtp.socketFactory or mail.smtp.socketFactory.class
)
) and
not exists(MethodAccess ma |
ma.getMethod() instanceof SetPropertyMethod and
ma.getQualifier() = propertiesVarAccess.getVariable().getAnAccess() and
(
getStringValue(ma.getArgument(0)).matches("%.ssl.checkserveridentity%") and //mail.smtp.ssl.checkserveridentity
getStringValue(ma.getArgument(1)) = "true"
)
)
}

/**
* Helper method to get string value of an argument
*/
string getStringValue(Expr expr) {
result = expr.(CompileTimeConstantExpr).getStringValue()
or
result = getStringValue(expr.(AddExpr).getLeftOperand())
or
result = getStringValue(expr.(AddExpr).getRightOperand())
}

/**
* The JavaMail session class `javax.mail.Session`
*/
class MailSession extends RefType {
MailSession() { this.hasQualifiedName("javax.mail", "Session") }
}

/**
* The class of Apache SimpleMail
*/
class SimpleMail extends RefType {
SimpleMail() { this.hasQualifiedName("org.apache.commons.mail", "SimpleEmail") }
}

/**
* Has TLS/SSL enabled with SimpleMail
*/
predicate enableTLSWithSimpleMail(MethodAccess ma) {
ma.getMethod().hasName("setSSLOnConnect") and
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true
}

/**
* Has no certificate check
*/
predicate hasNoCertCheckWithSimpleMail(VarAccess va) {
not exists(MethodAccess ma |
ma.getQualifier() = va.getVariable().getAnAccess() and
ma.getMethod().hasName("setSSLCheckServerIdentity") and
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true
)
}

from MethodAccess ma
where
ma.getMethod().getDeclaringType() instanceof MailSession and
ma.getMethod().getName() = "getInstance" and
isInsecureMailPropertyConfig(ma.getArgument(0))
or
enableTLSWithSimpleMail(ma) and hasNoCertCheckWithSimpleMail(ma.getQualifier())
select ma, "Java mailing has insecure SSL configuration"
43 changes: 43 additions & 0 deletions java/ql/src/experimental/Security/CWE/CWE-297/JavaMail.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import java.util.Properties;

import javax.activation.DataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;

import org.apache.logging.log4j.util.PropertiesUtil;

class JavaMail {
public static void main(String[] args) {
// BAD: Don't have server certificate check
{
final Properties properties = PropertiesUtil.getSystemProperties();
properties.put("mail.transport.protocol", "protocol");
properties.put("mail.smtp.host", "hostname");
properties.put("mail.smtp.socketFactory.class", "classname");

final Authenticator authenticator = buildAuthenticator("username", "password");
if (null != authenticator) {
properties.put("mail.smtp.auth", "true");
}
final Session session = Session.getInstance(properties, authenticator);
}

// GOOD: Have server certificate check
{
final Properties properties = PropertiesUtil.getSystemProperties();
properties.put("mail.transport.protocol", "protocol");
properties.put("mail.smtp.host", "hostname");
properties.put("mail.smtp.socketFactory.class", "classname");

final Authenticator authenticator = buildAuthenticator("username", "password");
if (null != authenticator) {
properties.put("mail.smtp.auth", "true");
properties.put("mail.smtp.ssl.checkserveridentity", "true");
}
final Session session = Session.getInstance(properties, authenticator);
}
}
}
40 changes: 40 additions & 0 deletions java/ql/src/experimental/Security/CWE/CWE-297/SimpleMail.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;

class SimpleMail {
public static void main(String[] args) throws EmailException {
// BAD: Don't have setSSLCheckServerIdentity set or set as false
{
Email email = new SimpleEmail();
email.setHostName("hostName");
email.setSmtpPort(25);
email.setAuthenticator(new DefaultAuthenticator("username", "password"));
email.setSSLOnConnect(true);

//email.setSSLCheckServerIdentity(false);
email.setFrom("fromAddress");
email.setSubject("subject");
email.setMsg("body");
email.addTo("toAddress");
email.send();
}

// GOOD: Have setSSLCheckServerIdentity set to true
{
Email email = new SimpleEmail();
email.setHostName("hostName");
email.setSmtpPort(25);
email.setAuthenticator(new DefaultAuthenticator("username", "password"));
email.setSSLOnConnect(true);

email.setSSLCheckServerIdentity(true);
email.setFrom("fromAddress");
email.setSubject("subject");
email.setMsg("body");
email.addTo("toAddress");
email.send();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
| InsecureJavaMail.java:29:27:29:72 | getInstance(...) | Java mailing has insecure SSL configuration |
| InsecureJavaMail.java:37:3:37:29 | setSSLOnConnect(...) | Java mailing has insecure SSL configuration |
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;

import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.SimpleEmail;

import java.util.Properties;

class InsecureJavaMail {
public void testJavaMail() {
final Properties properties = new Properties();
properties.put("mail.transport.protocol", "protocol");
properties.put("mail.smtp.host", "hostname");
properties.put("mail.smtp.socketFactory.class", "classname");

final javax.mail.Authenticator authenticator = new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password");
}
};
if (null != authenticator) {
properties.put("mail.smtp.auth", "true");
// properties.put("mail.smtp.ssl.checkserveridentity", "true");
}
final Session session = Session.getInstance(properties, authenticator);
}

public void testSimpleMail() {
Email email = new SimpleEmail();
email.setHostName("config.hostName");
email.setSmtpPort(25);
email.setAuthenticator(new DefaultAuthenticator("config.username", "config.password"));
email.setSSLOnConnect(true);
// email.setSSLCheckServerIdentity(true);
email.setFrom("fromAddress");
email.setSubject("subject");
email.setMsg("body");
email.addTo("toAddress");
email.send();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
experimental/Security/CWE/CWE-297/InsecureJavaMail.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-commons-email-1.6.0:${testdir}/../../../../stubs/javamail-api-1.6.2
Loading