mirror of
https://github.com/github/codeql.git
synced 2026-05-05 05:35:13 +02:00
Merge pull request #5538 from luchua-bc/java/credentials-in-properties
Java: CWE-555 Query to detect plaintext credentials in Java properties files
This commit is contained in:
@@ -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 application's 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
|
||||
keystores and password vaults can be used to encrypt and manage credentials.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the first example, the credentials for the LDAP and datasource properties are stored
|
||||
in cleartext in the properties file.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In the second example, the credentials for the LDAP and datasource properties are stored
|
||||
in an 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>
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note this query requires properties files to be indexed before it can produce results.
|
||||
* If creating your own database with the CodeQL CLI, you should run
|
||||
* `codeql database index-files --language=properties ...`
|
||||
* If using lgtm.com, you should add `properties_files: true` to the index block of your
|
||||
* lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction#customizing-index)
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.semmle.code.java.frameworks.CredentialsInPropertiesFile
|
||||
|
||||
/**
|
||||
* 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%"])
|
||||
}
|
||||
|
||||
from CredentialsConfig cc
|
||||
where not isNonProdCredentials(cc)
|
||||
select cc, cc.getConfigDesc()
|
||||
@@ -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==
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Provides classes for analyzing properties files.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.configfiles.ConfigFiles
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.Properties
|
||||
|
||||
private string possibleSecretName() {
|
||||
result =
|
||||
[
|
||||
"%password%", "%passwd%", "%account%", "%accnt%", "%credential%", "%token%", "%secret%",
|
||||
"%access%key%"
|
||||
]
|
||||
}
|
||||
|
||||
private string possibleEncryptedSecretName() { result = ["%hashed%", "%encrypted%", "%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
|
||||
// Could be a message property for UI display or fake passwords, e.g. login.password_expired=Your current password has expired.
|
||||
value.toLowerCase().matches(possibleSecretName())
|
||||
}
|
||||
|
||||
/** A configuration property that appears to contain a cleartext secret. */
|
||||
class CredentialsConfig extends ConfigPair {
|
||||
CredentialsConfig() {
|
||||
this.getNameElement().getName().trim().toLowerCase().matches(possibleSecretName()) and
|
||||
not this.getNameElement().getName().trim().toLowerCase().matches(possibleEncryptedSecretName()) and
|
||||
not isNotCleartextCredentials(this.getValueElement().getValue().trim())
|
||||
}
|
||||
|
||||
/** Gets the whitespace-trimmed name of this property. */
|
||||
string getName() { result = this.getNameElement().getName().trim() }
|
||||
|
||||
/** Gets the whitespace-trimmed value of this property. */
|
||||
string getValue() { result = this.getValueElement().getValue().trim() }
|
||||
|
||||
/** Returns a description of this vulnerability. */
|
||||
string getConfigDesc() {
|
||||
result =
|
||||
"Plaintext credentials " + this.getName() + " have cleartext value " + this.getValue() +
|
||||
" in properties 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 |
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Note this is similar to src/experimental/Security/CWE/CWE-555/CredentialsInPropertiesFile.ql
|
||||
* except we do not filter out test files.
|
||||
*/
|
||||
|
||||
import java
|
||||
import experimental.semmle.code.java.frameworks.CredentialsInPropertiesFile
|
||||
|
||||
from CredentialsConfig cc
|
||||
select cc, cc.getConfigDesc()
|
||||
@@ -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}
|
||||
@@ -0,0 +1,9 @@
|
||||
# GOOD: UI display messages; not credentials
|
||||
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.
|
||||
Reference in New Issue
Block a user