Query to detect plaintext credentials in Java properties files

This commit is contained in:
luchua-bc
2021-03-26 02:33:40 +00:00
parent 208d5157fa
commit d33b04cd96
7 changed files with 209 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
Credentials management issues occur when credentials are stored in plaintext in
an applications properties file. Common credentials include but are not limited
to LDAP, mail, database, proxy account, and so on. Storing plaintext credentials
in a properties file allows anyone who can read the file access to the protected
resource. Good credentials management guidelines require that credentials never
be stored in plaintext.
</p>
</overview>
<recommendation>
<p>
Credentials stored in properties files should be encrypted and recycled regularly.
In a Java EE deployment scenario, utilities provided by application servers like
keystore and password vault can be used to encrypt and manage credentials.
</p>
</recommendation>
<example>
<p>
In the first example, the credentials of a LDAP and datasource properties are stored
in cleartext in the properties file.
</p>
<p>
In the second example, the credentials of a LDAP and datasource properties are stored
in the encrypted format.
</p>
<sample src="configuration.properties" />
</example>
<references>
<li>
OWASP:
<a href="https://owasp.org/www-community/vulnerabilities/Password_Plaintext_Storage">Password Plaintext Storage</a>
</li>
<li>
Medium (Rajeev Shukla):
<a href="https://medium.com/@mail2rajeevshukla/hiding-encrypting-database-password-in-the-application-properties-34d59fe104eb">Encrypting database password in the application.properties file</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,87 @@
/**
* @name Cleartext Credentials in Properties File
* @description Finds cleartext credentials in Java properties files.
* @kind problem
* @id java/credentials-in-properties
* @tags security
* external/cwe/cwe-555
* external/cwe/cwe-256
* external/cwe/cwe-260
*/
import java
import semmle.code.configfiles.ConfigFiles
private string suspicious() {
result = "%password%" or
result = "%passwd%" or
result = "%account%" or
result = "%accnt%" or
result = "%credential%" or
result = "%token%" or
result = "%secret%" or
result = "%access%key%"
}
private string nonSuspicious() {
result = "%hashed%" or
result = "%encrypted%" or
result = "%crypt%"
}
/** Holds if the value is not cleartext credentials. */
bindingset[value]
predicate isNotCleartextCredentials(string value) {
value = "" // Empty string
or
value.length() < 7 // Typical credentials are no less than 6 characters
or
value.matches("% %") // Sentences containing spaces
or
value.regexpMatch(".*[^a-zA-Z\\d]{3,}.*") // Contain repeated non-alphanumeric characters such as a fake password pass**** or ????
or
value.matches("@%") // Starts with the "@" sign
or
value.regexpMatch("\\$\\{.*\\}") // Variable placeholder ${credentials}
or
value.matches("%=") // A basic check of encrypted credentials ending with padding characters
or
value.matches("ENC(%)") // Encrypted value
or
value.toLowerCase().matches(suspicious()) // Could be message properties or fake passwords
}
/**
* Holds if the credentials are in a non-production properties file indicated by:
* a) in a non-production directory
* b) with a non-production file name
*/
predicate isNonProdCredentials(CredentialsConfig cc) {
cc.getFile().getAbsolutePath().matches(["%dev%", "%test%", "%sample%"]) and
not cc.getFile().getAbsolutePath().matches("%codeql%") // CodeQL test cases
}
/** The properties file with configuration key/value pairs. */
class ConfigProperties extends ConfigPair {
ConfigProperties() { this.getFile().getBaseName().toLowerCase().matches("%.properties") }
}
/** The credentials configuration property. */
class CredentialsConfig extends ConfigProperties {
CredentialsConfig() {
this.getNameElement().getName().trim().toLowerCase().matches(suspicious()) and
not this.getNameElement().getName().trim().toLowerCase().matches(nonSuspicious())
}
string getName() { result = this.getNameElement().getName().trim() }
string getValue() { result = this.getValueElement().getValue().trim() }
}
from CredentialsConfig cc
where
not isNotCleartextCredentials(cc.getValue()) and
not isNonProdCredentials(cc)
select cc,
"Plaintext credentials " + cc.getName() + " have cleartext value " + cc.getValue() +
" in properties file."

View File

@@ -0,0 +1,26 @@
#***************************** LDAP Credentials *****************************************#
ldap.ldapHost = ldap.example.com
ldap.ldapPort = 636
ldap.loginDN = cn=Directory Manager
#### BAD: LDAP credentials are stored in cleartext ####
ldap.password = mysecpass
#### GOOD: LDAP credentials are stored in the encrypted format ####
ldap.password = eFRZ3Cqo5zDJWMYLiaEupw==
ldap.domain1 = example
ldap.domain2 = com
ldap.url= ldaps://ldap.example.com:636/dc=example,dc=com
#*************************** MS SQL Database Connection **********************************#
datasource1.driverClassName = com.microsoft.sqlserver.jdbc.SQLServerDriver
datasource1.url = jdbc:sqlserver://ms.example.com\\exampledb:1433;
datasource1.username = sa
#### BAD: Datasource credentials are stored in cleartext ####
datasource1.password = Passw0rd@123
#### GOOD: Datasource credentials are stored in the encrypted format ####
datasource1.password = VvOgflYS1EUzJdVNDoBcnA==

View File

@@ -0,0 +1,5 @@
| configuration.properties:6:1:6:25 | ldap.password=mysecpass | Plaintext credentials ldap.password have cleartext value mysecpass in properties file. |
| configuration.properties:18:1:18:35 | datasource1.password=Passw0rd@123 | Plaintext credentials datasource1.password have cleartext value Passw0rd@123 in properties file. |
| configuration.properties:25:1:25:31 | mail.password=MysecPWxWa@1993 | Plaintext credentials mail.password have cleartext value MysecPWxWa@1993 in properties file. |
| 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. |
| 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. |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-555/CredentialsInPropertiesFile.ql

View File

@@ -0,0 +1,37 @@
#***************************** LDAP Credentials *****************************************#
ldap.ldapHost = ldap.example.com
ldap.ldapPort = 636
ldap.loginDN = cn=Directory Manager
#### BAD: LDAP credentials are stored in cleartext ####
ldap.password = mysecpass
#### GOOD: LDAP credentials are stored in the encrypted format ####
ldap.password = eFRZ3Cqo5zDJWMYLiaEupw==
ldap.domain1 = example
ldap.domain2 = com
ldap.url= ldaps://ldap.example.com:636/dc=example,dc=com
#*************************** MS SQL Database Connection **********************************#
datasource1.driverClassName = com.microsoft.sqlserver.jdbc.SQLServerDriver
datasource1.url = jdbc:sqlserver://ms.example.com\\exampledb:1433;
datasource1.username = sa
#### BAD: Datasource credentials are stored in cleartext ####
datasource1.password = Passw0rd@123
#### GOOD: Datasource credentials are stored in the encrypted format ####
datasource1.password = VvOgflYS1EUzJdVNDoBcnA==
#*************************** Mail Connection **********************************#
mail.username = test@example.com
#### BAD: Mail credentials are stored in cleartext ####
mail.password = MysecPWxWa@1993
#### GOOD: Mail credentials are stored in the encrypted format ####
mail.password = M*********@1993
#*************************** AWS S3 Connection **********************************#
com.example.aws.s3.bucket_name=com-bucket-1
com.example.aws.s3.directory_name=com-directory-1
#### BAD: Access keys are stored in properties file in cleartext ####
com.example.aws.s3.access_key=AKMAMQPBYMCD6YSAYCBA
com.example.aws.s3.secret_key=8lMPSfWzZq+wcWtck5+QPLOJDZzE783pS09/IO3k
#### GOOD: Access keys are not stored in properties file ####
com.example.aws.s3.access_key=${ENV:AWS_ACCESS_KEY_ID}
com.example.aws.s3.secret_key=${ENV:AWS_SECRET_ACCESS_KEY}

View File

@@ -0,0 +1,8 @@
prompt.username=Username
prompt.password=Password
forgot_password.error=Please enter a valid email address.
reset_password.error=Passwords must match and not be empty.
login.password_expired=Your current password has expired. Please reset your password.
login.login_failure=Unable to verify username or password. Please try again.