Skip to content

Commit 2656a52

Browse files
authored
Merge pull request github#5538 from luchua-bc/java/credentials-in-properties
Java: CWE-555 Query to detect plaintext credentials in Java properties files
2 parents abeefca + c281e54 commit 2656a52

File tree

8 files changed

+229
-0
lines changed

8 files changed

+229
-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+
keystores and password vaults can be used to encrypt and manage credentials.
19+
</p>
20+
</recommendation>
21+
22+
<example>
23+
<p>
24+
In the first example, the credentials for the 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 for the LDAP and datasource properties are stored
30+
in an 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: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
/*
13+
* Note this query requires properties files to be indexed before it can produce results.
14+
* If creating your own database with the CodeQL CLI, you should run
15+
* `codeql database index-files --language=properties ...`
16+
* If using lgtm.com, you should add `properties_files: true` to the index block of your
17+
* lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction#customizing-index)
18+
*/
19+
20+
import java
21+
import experimental.semmle.code.java.frameworks.CredentialsInPropertiesFile
22+
23+
/**
24+
* Holds if the credentials are in a non-production properties file indicated by:
25+
* a) in a non-production directory
26+
* b) with a non-production file name
27+
*/
28+
predicate isNonProdCredentials(CredentialsConfig cc) {
29+
cc.getFile().getAbsolutePath().matches(["%dev%", "%test%", "%sample%"])
30+
}
31+
32+
from CredentialsConfig cc
33+
where not isNonProdCredentials(cc)
34+
select cc, cc.getConfigDesc()
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: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Provides classes for analyzing properties files.
3+
*/
4+
5+
import java
6+
import semmle.code.configfiles.ConfigFiles
7+
import semmle.code.java.dataflow.FlowSources
8+
import semmle.code.java.frameworks.Properties
9+
10+
private string possibleSecretName() {
11+
result =
12+
[
13+
"%password%", "%passwd%", "%account%", "%accnt%", "%credential%", "%token%", "%secret%",
14+
"%access%key%"
15+
]
16+
}
17+
18+
private string possibleEncryptedSecretName() { result = ["%hashed%", "%encrypted%", "%crypt%"] }
19+
20+
/** Holds if the value is not cleartext credentials. */
21+
bindingset[value]
22+
predicate isNotCleartextCredentials(string value) {
23+
value = "" // Empty string
24+
or
25+
value.length() < 7 // Typical credentials are no less than 6 characters
26+
or
27+
value.matches("% %") // Sentences containing spaces
28+
or
29+
value.regexpMatch(".*[^a-zA-Z\\d]{3,}.*") // Contain repeated non-alphanumeric characters such as a fake password pass**** or ????
30+
or
31+
value.matches("@%") // Starts with the "@" sign
32+
or
33+
value.regexpMatch("\\$\\{.*\\}") // Variable placeholder ${credentials}
34+
or
35+
value.matches("%=") // A basic check of encrypted credentials ending with padding characters
36+
or
37+
value.matches("ENC(%)") // Encrypted value
38+
or
39+
// Could be a message property for UI display or fake passwords, e.g. login.password_expired=Your current password has expired.
40+
value.toLowerCase().matches(possibleSecretName())
41+
}
42+
43+
/** A configuration property that appears to contain a cleartext secret. */
44+
class CredentialsConfig extends ConfigPair {
45+
CredentialsConfig() {
46+
this.getNameElement().getName().trim().toLowerCase().matches(possibleSecretName()) and
47+
not this.getNameElement().getName().trim().toLowerCase().matches(possibleEncryptedSecretName()) and
48+
not isNotCleartextCredentials(this.getValueElement().getValue().trim())
49+
}
50+
51+
/** Gets the whitespace-trimmed name of this property. */
52+
string getName() { result = this.getNameElement().getName().trim() }
53+
54+
/** Gets the whitespace-trimmed value of this property. */
55+
string getValue() { result = this.getValueElement().getValue().trim() }
56+
57+
/** Returns a description of this vulnerability. */
58+
string getConfigDesc() {
59+
result =
60+
"Plaintext credentials " + this.getName() + " have cleartext value " + this.getValue() +
61+
" in properties file"
62+
}
63+
}
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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Note this is similar to src/experimental/Security/CWE/CWE-555/CredentialsInPropertiesFile.ql
3+
* except we do not filter out test files.
4+
*/
5+
6+
import java
7+
import experimental.semmle.code.java.frameworks.CredentialsInPropertiesFile
8+
9+
from CredentialsConfig cc
10+
select cc, cc.getConfigDesc()
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# GOOD: UI display messages; not credentials
2+
prompt.username=Username
3+
prompt.password=Password
4+
5+
forgot_password.error=Please enter a valid email address.
6+
reset_password.error=Passwords must match and not be empty.
7+
8+
login.password_expired=Your current password has expired. Please reset your password.
9+
login.login_failure=Unable to verify username or password. Please try again.

0 commit comments

Comments
 (0)