Skip to content

Commit d33b04c

Browse files
committed
Query to detect plaintext credentials in Java properties files
1 parent 208d515 commit d33b04c

File tree

7 files changed

+209
-0
lines changed

7 files changed

+209
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<p>
5+
Credentials management issues occur when credentials are stored in plaintext in
6+
an application’s properties file. Common credentials include but are not limited
7+
to LDAP, mail, database, proxy account, and so on. Storing plaintext credentials
8+
in a properties file allows anyone who can read the file access to the protected
9+
resource. Good credentials management guidelines require that credentials never
10+
be stored in plaintext.
11+
</p>
12+
</overview>
13+
14+
<recommendation>
15+
<p>
16+
Credentials stored in properties files should be encrypted and recycled regularly.
17+
In a Java EE deployment scenario, utilities provided by application servers like
18+
keystore and password vault can be used to encrypt and manage credentials.
19+
</p>
20+
</recommendation>
21+
22+
<example>
23+
<p>
24+
In the first example, the credentials of a LDAP and datasource properties are stored
25+
in cleartext in the properties file.
26+
</p>
27+
28+
<p>
29+
In the second example, the credentials of a LDAP and datasource properties are stored
30+
in the encrypted format.
31+
</p>
32+
<sample src="configuration.properties" />
33+
</example>
34+
35+
<references>
36+
<li>
37+
OWASP:
38+
<a href="https://owasp.org/www-community/vulnerabilities/Password_Plaintext_Storage">Password Plaintext Storage</a>
39+
</li>
40+
<li>
41+
Medium (Rajeev Shukla):
42+
<a href="https://medium.com/@mail2rajeevshukla/hiding-encrypting-database-password-in-the-application-properties-34d59fe104eb">Encrypting database password in the application.properties file</a>
43+
</li>
44+
</references>
45+
</qhelp>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @name Cleartext Credentials in Properties File
3+
* @description Finds cleartext credentials in Java properties files.
4+
* @kind problem
5+
* @id java/credentials-in-properties
6+
* @tags security
7+
* external/cwe/cwe-555
8+
* external/cwe/cwe-256
9+
* external/cwe/cwe-260
10+
*/
11+
12+
import java
13+
import semmle.code.configfiles.ConfigFiles
14+
15+
private string suspicious() {
16+
result = "%password%" or
17+
result = "%passwd%" or
18+
result = "%account%" or
19+
result = "%accnt%" or
20+
result = "%credential%" or
21+
result = "%token%" or
22+
result = "%secret%" or
23+
result = "%access%key%"
24+
}
25+
26+
private string nonSuspicious() {
27+
result = "%hashed%" or
28+
result = "%encrypted%" or
29+
result = "%crypt%"
30+
}
31+
32+
/** Holds if the value is not cleartext credentials. */
33+
bindingset[value]
34+
predicate isNotCleartextCredentials(string value) {
35+
value = "" // Empty string
36+
or
37+
value.length() < 7 // Typical credentials are no less than 6 characters
38+
or
39+
value.matches("% %") // Sentences containing spaces
40+
or
41+
value.regexpMatch(".*[^a-zA-Z\\d]{3,}.*") // Contain repeated non-alphanumeric characters such as a fake password pass**** or ????
42+
or
43+
value.matches("@%") // Starts with the "@" sign
44+
or
45+
value.regexpMatch("\\$\\{.*\\}") // Variable placeholder ${credentials}
46+
or
47+
value.matches("%=") // A basic check of encrypted credentials ending with padding characters
48+
or
49+
value.matches("ENC(%)") // Encrypted value
50+
or
51+
value.toLowerCase().matches(suspicious()) // Could be message properties or fake passwords
52+
}
53+
54+
/**
55+
* Holds if the credentials are in a non-production properties file indicated by:
56+
* a) in a non-production directory
57+
* b) with a non-production file name
58+
*/
59+
predicate isNonProdCredentials(CredentialsConfig cc) {
60+
cc.getFile().getAbsolutePath().matches(["%dev%", "%test%", "%sample%"]) and
61+
not cc.getFile().getAbsolutePath().matches("%codeql%") // CodeQL test cases
62+
}
63+
64+
/** The properties file with configuration key/value pairs. */
65+
class ConfigProperties extends ConfigPair {
66+
ConfigProperties() { this.getFile().getBaseName().toLowerCase().matches("%.properties") }
67+
}
68+
69+
/** The credentials configuration property. */
70+
class CredentialsConfig extends ConfigProperties {
71+
CredentialsConfig() {
72+
this.getNameElement().getName().trim().toLowerCase().matches(suspicious()) and
73+
not this.getNameElement().getName().trim().toLowerCase().matches(nonSuspicious())
74+
}
75+
76+
string getName() { result = this.getNameElement().getName().trim() }
77+
78+
string getValue() { result = this.getValueElement().getValue().trim() }
79+
}
80+
81+
from CredentialsConfig cc
82+
where
83+
not isNotCleartextCredentials(cc.getValue()) and
84+
not isNonProdCredentials(cc)
85+
select cc,
86+
"Plaintext credentials " + cc.getName() + " have cleartext value " + cc.getValue() +
87+
" in properties file."
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#***************************** LDAP Credentials *****************************************#
2+
3+
ldap.ldapHost = ldap.example.com
4+
ldap.ldapPort = 636
5+
ldap.loginDN = cn=Directory Manager
6+
7+
#### BAD: LDAP credentials are stored in cleartext ####
8+
ldap.password = mysecpass
9+
10+
#### GOOD: LDAP credentials are stored in the encrypted format ####
11+
ldap.password = eFRZ3Cqo5zDJWMYLiaEupw==
12+
13+
ldap.domain1 = example
14+
ldap.domain2 = com
15+
ldap.url= ldaps://ldap.example.com:636/dc=example,dc=com
16+
17+
#*************************** MS SQL Database Connection **********************************#
18+
datasource1.driverClassName = com.microsoft.sqlserver.jdbc.SQLServerDriver
19+
datasource1.url = jdbc:sqlserver://ms.example.com\\exampledb:1433;
20+
datasource1.username = sa
21+
22+
#### BAD: Datasource credentials are stored in cleartext ####
23+
datasource1.password = Passw0rd@123
24+
25+
#### GOOD: Datasource credentials are stored in the encrypted format ####
26+
datasource1.password = VvOgflYS1EUzJdVNDoBcnA==
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| configuration.properties:6:1:6:25 | ldap.password=mysecpass | Plaintext credentials ldap.password have cleartext value mysecpass in properties file. |
2+
| configuration.properties:18:1:18:35 | datasource1.password=Passw0rd@123 | Plaintext credentials datasource1.password have cleartext value Passw0rd@123 in properties file. |
3+
| configuration.properties:25:1:25:31 | mail.password=MysecPWxWa@1993 | Plaintext credentials mail.password have cleartext value MysecPWxWa@1993 in properties file. |
4+
| configuration.properties:33:1:33:50 | com.example.aws.s3.access_key=AKMAMQPBYMCD6YSAYCBA | Plaintext credentials com.example.aws.s3.access_key have cleartext value AKMAMQPBYMCD6YSAYCBA in properties file. |
5+
| configuration.properties:34:1:34:70 | com.example.aws.s3.secret_key=8lMPSfWzZq+wcWtck5+QPLOJDZzE783pS09/IO3k | Plaintext credentials com.example.aws.s3.secret_key have cleartext value 8lMPSfWzZq+wcWtck5+QPLOJDZzE783pS09/IO3k in properties file. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-555/CredentialsInPropertiesFile.ql
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#***************************** LDAP Credentials *****************************************#
2+
ldap.ldapHost = ldap.example.com
3+
ldap.ldapPort = 636
4+
ldap.loginDN = cn=Directory Manager
5+
#### BAD: LDAP credentials are stored in cleartext ####
6+
ldap.password = mysecpass
7+
#### GOOD: LDAP credentials are stored in the encrypted format ####
8+
ldap.password = eFRZ3Cqo5zDJWMYLiaEupw==
9+
ldap.domain1 = example
10+
ldap.domain2 = com
11+
ldap.url= ldaps://ldap.example.com:636/dc=example,dc=com
12+
13+
#*************************** MS SQL Database Connection **********************************#
14+
datasource1.driverClassName = com.microsoft.sqlserver.jdbc.SQLServerDriver
15+
datasource1.url = jdbc:sqlserver://ms.example.com\\exampledb:1433;
16+
datasource1.username = sa
17+
#### BAD: Datasource credentials are stored in cleartext ####
18+
datasource1.password = Passw0rd@123
19+
#### GOOD: Datasource credentials are stored in the encrypted format ####
20+
datasource1.password = VvOgflYS1EUzJdVNDoBcnA==
21+
22+
#*************************** Mail Connection **********************************#
23+
mail.username = [email protected]
24+
#### BAD: Mail credentials are stored in cleartext ####
25+
mail.password = MysecPWxWa@1993
26+
#### GOOD: Mail credentials are stored in the encrypted format ####
27+
mail.password = M*********@1993
28+
29+
#*************************** AWS S3 Connection **********************************#
30+
com.example.aws.s3.bucket_name=com-bucket-1
31+
com.example.aws.s3.directory_name=com-directory-1
32+
#### BAD: Access keys are stored in properties file in cleartext ####
33+
com.example.aws.s3.access_key=AKMAMQPBYMCD6YSAYCBA
34+
com.example.aws.s3.secret_key=8lMPSfWzZq+wcWtck5+QPLOJDZzE783pS09/IO3k
35+
#### GOOD: Access keys are not stored in properties file ####
36+
com.example.aws.s3.access_key=${ENV:AWS_ACCESS_KEY_ID}
37+
com.example.aws.s3.secret_key=${ENV:AWS_SECRET_ACCESS_KEY}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
prompt.username=Username
2+
prompt.password=Password
3+
4+
forgot_password.error=Please enter a valid email address.
5+
reset_password.error=Passwords must match and not be empty.
6+
7+
login.password_expired=Your current password has expired. Please reset your password.
8+
login.login_failure=Unable to verify username or password. Please try again.

0 commit comments

Comments
 (0)