Skip to content

Commit 4b3ca13

Browse files
authored
Merge pull request github#3491 from luchua-bc/java-insecure-smtp-ssl
Java: CWE-297 insecure JavaMail SSL configuration
2 parents 06066f0 + 1fd9c7f commit 4b3ca13

File tree

17 files changed

+2783
-0
lines changed

17 files changed

+2783
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<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>
8+
<p>This query checks whether SSL certificate is validated when username/password is sent in authenticator and when SSL is enabled.</p>
9+
<p>The query has code for both plain JavaMail invocation and mailing through Apache SimpleMail to make it more comprehensive.</p>
10+
</overview>
11+
12+
<recommendation>
13+
<p>Validate SSL certificate when sensitive information is sent in email communications.</p>
14+
</recommendation>
15+
16+
<example>
17+
<p>The following two examples show two ways of configuring secure emails through JavaMail or Apache SimpleMail. In the 'BAD' case,
18+
credentials are sent in an SSL session without certificate validation. In the 'GOOD' case, the certificate is validated.</p>
19+
<sample src="JavaMail.java" />
20+
<sample src="SimpleMail.java" />
21+
</example>
22+
23+
<references>
24+
<li>
25+
<a href="https://cwe.mitre.org/data/definitions/297.html">CWE-297</a>
26+
<a href="https://issues.apache.org/jira/browse/LOG4J2-2819">Add support for specifying an SSL configuration for SmtpAppender (CVE-2020-9488)</a>
27+
<a href="https://rules.sonarsource.com/java/tag/owasp/RSPEC-4499">SMTP SSL connection should check server identity</a>
28+
</li>
29+
</references>
30+
</qhelp>
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* @id java/insecure-smtp-ssl
3+
* @name Insecure JavaMail SSL Configuration
4+
* @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.
5+
* @kind problem
6+
* @tags security
7+
* external/cwe-297
8+
*/
9+
10+
import java
11+
12+
/**
13+
* The method to set Java properties
14+
*/
15+
class SetPropertyMethod extends Method {
16+
SetPropertyMethod() {
17+
this.hasName("setProperty") and
18+
this.getDeclaringType().hasQualifiedName("java.util", "Properties")
19+
or
20+
this.hasName("put") and
21+
this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.util", "Dictionary")
22+
}
23+
}
24+
25+
/**
26+
* The insecure way to set Java properties in mail sessions.
27+
* 1. Set the mail.smtp.auth property to provide the SMTP Transport with a username and password when connecting to the SMTP server or
28+
* set the mail.smtp.ssl.socketFactory/mail.smtp.ssl.socketFactory.class property to create an SMTP SSL socket.
29+
* 2. No mail.smtp.ssl.checkserveridentity property is enabled.
30+
*/
31+
predicate isInsecureMailPropertyConfig(VarAccess propertiesVarAccess) {
32+
exists(MethodAccess ma |
33+
ma.getMethod() instanceof SetPropertyMethod and
34+
ma.getQualifier() = propertiesVarAccess.getVariable().getAnAccess() and
35+
(
36+
getStringValue(ma.getArgument(0)).matches("%.auth%") and //mail.smtp.auth
37+
getStringValue(ma.getArgument(1)) = "true"
38+
or
39+
getStringValue(ma.getArgument(0)).matches("%.socketFactory%") //mail.smtp.socketFactory or mail.smtp.socketFactory.class
40+
)
41+
) and
42+
not exists(MethodAccess ma |
43+
ma.getMethod() instanceof SetPropertyMethod and
44+
ma.getQualifier() = propertiesVarAccess.getVariable().getAnAccess() and
45+
(
46+
getStringValue(ma.getArgument(0)).matches("%.ssl.checkserveridentity%") and //mail.smtp.ssl.checkserveridentity
47+
getStringValue(ma.getArgument(1)) = "true"
48+
)
49+
)
50+
}
51+
52+
/**
53+
* Helper method to get string value of an argument
54+
*/
55+
string getStringValue(Expr expr) {
56+
result = expr.(CompileTimeConstantExpr).getStringValue()
57+
or
58+
result = getStringValue(expr.(AddExpr).getLeftOperand())
59+
or
60+
result = getStringValue(expr.(AddExpr).getRightOperand())
61+
}
62+
63+
/**
64+
* The JavaMail session class `javax.mail.Session`
65+
*/
66+
class MailSession extends RefType {
67+
MailSession() { this.hasQualifiedName("javax.mail", "Session") }
68+
}
69+
70+
/**
71+
* The class of Apache SimpleMail
72+
*/
73+
class SimpleMail extends RefType {
74+
SimpleMail() { this.hasQualifiedName("org.apache.commons.mail", "SimpleEmail") }
75+
}
76+
77+
/**
78+
* Has TLS/SSL enabled with SimpleMail
79+
*/
80+
predicate enableTLSWithSimpleMail(MethodAccess ma) {
81+
ma.getMethod().hasName("setSSLOnConnect") and
82+
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true
83+
}
84+
85+
/**
86+
* Has no certificate check
87+
*/
88+
predicate hasNoCertCheckWithSimpleMail(VarAccess va) {
89+
not exists(MethodAccess ma |
90+
ma.getQualifier() = va.getVariable().getAnAccess() and
91+
ma.getMethod().hasName("setSSLCheckServerIdentity") and
92+
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true
93+
)
94+
}
95+
96+
from MethodAccess ma
97+
where
98+
ma.getMethod().getDeclaringType() instanceof MailSession and
99+
ma.getMethod().getName() = "getInstance" and
100+
isInsecureMailPropertyConfig(ma.getArgument(0))
101+
or
102+
enableTLSWithSimpleMail(ma) and hasNoCertCheckWithSimpleMail(ma.getQualifier())
103+
select ma, "Java mailing has insecure SSL configuration"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import java.util.Properties;
2+
3+
import javax.activation.DataSource;
4+
import javax.mail.Authenticator;
5+
import javax.mail.Message;
6+
import javax.mail.MessagingException;
7+
import javax.mail.PasswordAuthentication;
8+
import javax.mail.Session;
9+
10+
import org.apache.logging.log4j.util.PropertiesUtil;
11+
12+
class JavaMail {
13+
public static void main(String[] args) {
14+
// BAD: Don't have server certificate check
15+
{
16+
final Properties properties = PropertiesUtil.getSystemProperties();
17+
properties.put("mail.transport.protocol", "protocol");
18+
properties.put("mail.smtp.host", "hostname");
19+
properties.put("mail.smtp.socketFactory.class", "classname");
20+
21+
final Authenticator authenticator = buildAuthenticator("username", "password");
22+
if (null != authenticator) {
23+
properties.put("mail.smtp.auth", "true");
24+
}
25+
final Session session = Session.getInstance(properties, authenticator);
26+
}
27+
28+
// GOOD: Have server certificate check
29+
{
30+
final Properties properties = PropertiesUtil.getSystemProperties();
31+
properties.put("mail.transport.protocol", "protocol");
32+
properties.put("mail.smtp.host", "hostname");
33+
properties.put("mail.smtp.socketFactory.class", "classname");
34+
35+
final Authenticator authenticator = buildAuthenticator("username", "password");
36+
if (null != authenticator) {
37+
properties.put("mail.smtp.auth", "true");
38+
properties.put("mail.smtp.ssl.checkserveridentity", "true");
39+
}
40+
final Session session = Session.getInstance(properties, authenticator);
41+
}
42+
}
43+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import org.apache.commons.mail.DefaultAuthenticator;
2+
import org.apache.commons.mail.Email;
3+
import org.apache.commons.mail.EmailException;
4+
import org.apache.commons.mail.SimpleEmail;
5+
6+
class SimpleMail {
7+
public static void main(String[] args) throws EmailException {
8+
// BAD: Don't have setSSLCheckServerIdentity set or set as false
9+
{
10+
Email email = new SimpleEmail();
11+
email.setHostName("hostName");
12+
email.setSmtpPort(25);
13+
email.setAuthenticator(new DefaultAuthenticator("username", "password"));
14+
email.setSSLOnConnect(true);
15+
16+
//email.setSSLCheckServerIdentity(false);
17+
email.setFrom("fromAddress");
18+
email.setSubject("subject");
19+
email.setMsg("body");
20+
email.addTo("toAddress");
21+
email.send();
22+
}
23+
24+
// GOOD: Have setSSLCheckServerIdentity set to true
25+
{
26+
Email email = new SimpleEmail();
27+
email.setHostName("hostName");
28+
email.setSmtpPort(25);
29+
email.setAuthenticator(new DefaultAuthenticator("username", "password"));
30+
email.setSSLOnConnect(true);
31+
32+
email.setSSLCheckServerIdentity(true);
33+
email.setFrom("fromAddress");
34+
email.setSubject("subject");
35+
email.setMsg("body");
36+
email.addTo("toAddress");
37+
email.send();
38+
}
39+
}
40+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| InsecureJavaMail.java:29:27:29:72 | getInstance(...) | Java mailing has insecure SSL configuration |
2+
| InsecureJavaMail.java:37:3:37:29 | setSSLOnConnect(...) | Java mailing has insecure SSL configuration |
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import java.util.Properties;
2+
3+
import javax.mail.Authenticator;
4+
import javax.mail.PasswordAuthentication;
5+
import javax.mail.Session;
6+
7+
import org.apache.commons.mail.DefaultAuthenticator;
8+
import org.apache.commons.mail.Email;
9+
import org.apache.commons.mail.SimpleEmail;
10+
11+
import java.util.Properties;
12+
13+
class InsecureJavaMail {
14+
public void testJavaMail() {
15+
final Properties properties = new Properties();
16+
properties.put("mail.transport.protocol", "protocol");
17+
properties.put("mail.smtp.host", "hostname");
18+
properties.put("mail.smtp.socketFactory.class", "classname");
19+
20+
final javax.mail.Authenticator authenticator = new javax.mail.Authenticator() {
21+
protected PasswordAuthentication getPasswordAuthentication() {
22+
return new PasswordAuthentication("username", "password");
23+
}
24+
};
25+
if (null != authenticator) {
26+
properties.put("mail.smtp.auth", "true");
27+
// properties.put("mail.smtp.ssl.checkserveridentity", "true");
28+
}
29+
final Session session = Session.getInstance(properties, authenticator);
30+
}
31+
32+
public void testSimpleMail() {
33+
Email email = new SimpleEmail();
34+
email.setHostName("config.hostName");
35+
email.setSmtpPort(25);
36+
email.setAuthenticator(new DefaultAuthenticator("config.username", "config.password"));
37+
email.setSSLOnConnect(true);
38+
// email.setSSLCheckServerIdentity(true);
39+
email.setFrom("fromAddress");
40+
email.setSubject("subject");
41+
email.setMsg("body");
42+
email.addTo("toAddress");
43+
email.send();
44+
}
45+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-297/InsecureJavaMail.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-commons-email-1.6.0:${testdir}/../../../../stubs/javamail-api-1.6.2

0 commit comments

Comments
 (0)