From 2b814963d3f230b42742183039cd6a9c87d30bb5 Mon Sep 17 00:00:00 2001 From: gaetan-ferry-sonarsource <112399173+gaetan-ferry-sonarsource@users.noreply.github.com> Date: Fri, 29 Sep 2023 14:44:00 +0200 Subject: [PATCH] Modify S6437: Change text to LaYC format (APPSEC-1111) (#3165) --- rules/S6437/common/fix/code-rationale.adoc | 2 + rules/S6437/common/fix/how-it-works.adoc | 9 ++ rules/S6437/common/impact/rationale.adoc | 2 + .../S6437/common/resources/documentation.adoc | 6 ++ rules/S6437/common/resources/standards.adoc | 6 ++ rules/S6437/description.adoc | 32 ------ rules/S6437/docker/rule.adoc | 98 +++++++++++-------- rules/S6437/java/rule.adoc | 83 +++++++--------- rules/S6437/php/rule.adoc | 75 +++++++------- rules/S6437/python/rule.adoc | 96 +++++++----------- 10 files changed, 190 insertions(+), 219 deletions(-) create mode 100644 rules/S6437/common/fix/code-rationale.adoc create mode 100644 rules/S6437/common/fix/how-it-works.adoc create mode 100644 rules/S6437/common/impact/rationale.adoc create mode 100644 rules/S6437/common/resources/documentation.adoc create mode 100644 rules/S6437/common/resources/standards.adoc delete mode 100644 rules/S6437/description.adoc diff --git a/rules/S6437/common/fix/code-rationale.adoc b/rules/S6437/common/fix/code-rationale.adoc new file mode 100644 index 0000000000..989713f73d --- /dev/null +++ b/rules/S6437/common/fix/code-rationale.adoc @@ -0,0 +1,2 @@ +The following code example is noncompliant because it uses a hardcoded secret +value. \ No newline at end of file diff --git a/rules/S6437/common/fix/how-it-works.adoc b/rules/S6437/common/fix/how-it-works.adoc new file mode 100644 index 0000000000..3cccad737d --- /dev/null +++ b/rules/S6437/common/fix/how-it-works.adoc @@ -0,0 +1,9 @@ +=== How does this work? + +While the noncompliant code example contains a hard-coded password, the +compliant solution retrieves the secret's value from its environment. This +allows to have an environment-dependent secret value and avoids storing the +password in the source code itself. + +Depending on the application and its underlying infrastructure, how the secret +gets added to the environment might change. \ No newline at end of file diff --git a/rules/S6437/common/impact/rationale.adoc b/rules/S6437/common/impact/rationale.adoc new file mode 100644 index 0000000000..bf27fcf15a --- /dev/null +++ b/rules/S6437/common/impact/rationale.adoc @@ -0,0 +1,2 @@ +The consequences vary greatly depending on the situation and the secret-exposed +audience. Still, two main scenarios should be considered. diff --git a/rules/S6437/common/resources/documentation.adoc b/rules/S6437/common/resources/documentation.adoc new file mode 100644 index 0000000000..c66531214a --- /dev/null +++ b/rules/S6437/common/resources/documentation.adoc @@ -0,0 +1,6 @@ +=== Documentation + +* AWS Documentation - https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html[What is AWS Secrets Manager] +* Azure Documentation - https://learn.microsoft.com/en-us/azure/key-vault/[Azure Key Vault] +* Google Cloud - https://cloud.google.com/secret-manager/docs[Secret Manager documentation] +* HashiCorp Developer - https://developer.hashicorp.com/vault/docs[Vault Documentation] \ No newline at end of file diff --git a/rules/S6437/common/resources/standards.adoc b/rules/S6437/common/resources/standards.adoc new file mode 100644 index 0000000000..63b583867d --- /dev/null +++ b/rules/S6437/common/resources/standards.adoc @@ -0,0 +1,6 @@ +=== Standards + +* OWASP - https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/[Top 10 2021 - Category A7 - Identification and Authentication Failures] +* OWASP - https://www.owasp.org/index.php/Top_10-2017_A2-Broken_Authentication[Top 10 2017 - Category A2 - Broken Authentication] +* CWE - https://cwe.mitre.org/data/definitions/798.html[CWE-798 - Use of Hard-coded Credentials] +* CWE - https://cwe.mitre.org/data/definitions/259.html[CWE-259 - Use of Hard-coded Password] \ No newline at end of file diff --git a/rules/S6437/description.adoc b/rules/S6437/description.adoc deleted file mode 100644 index e8352349e3..0000000000 --- a/rules/S6437/description.adoc +++ /dev/null @@ -1,32 +0,0 @@ -A hard-coded secret has been found in your code. You should quickly list where -this secret is used, revoke it, and then change it in every system that uses -it. - -Passwords, secrets, and any type of credentials should only be used to -authenticate a single entity (a person or a system). - -If you allow third parties to authenticate as another system or person, they -can impersonate legitimate identities and undermine trust within the -organization. + -It does not matter if the impersonation is malicious: In either case, it is a -clear breach of trust in the system, as the systems involved falsely assume -that the authenticated entity is who it claims to be. + -The consequences can be catastrophic. - -Keeping credentials in plain text in a code base is tantamount to sharing that -password with anyone who has access to the source code and runtime servers. + -Thus, it is a breach of trust, as these individuals have the ability to -impersonate others. - -Secret management services are the most efficient tools to store credentials -and protect the identities associated with them. + -Cloud providers and on-premise services can be used for this purpose. - -If storing credentials in a secret data management service is not possible, -follow these guidelines: - -* Do not store credentials in a file that an excessive number of people can access. -** For example, not in code, not in a spreadsheet, not on a sticky note, and not on a shared drive. -* Use the production operating system to protect password access control. -** For example, in a file whose permissions are restricted and protected with chmod and chown. - diff --git a/rules/S6437/docker/rule.adoc b/rules/S6437/docker/rule.adoc index eddb888d99..1a827e746f 100644 --- a/rules/S6437/docker/rule.adoc +++ b/rules/S6437/docker/rule.adoc @@ -1,36 +1,47 @@ +include::../../../shared_content/secrets/description.adoc[] + == Why is this an issue? -Sensitive data has been found in the Dockerfile or Docker image. The data -should be considered breached. +include::../../../shared_content/secrets/rationale.adoc[] -If malicious third parties can get a hold of such information, they could -impersonate legitimate identities within the organization. + -It is a clear breach of trust in the system, as the systems involved falsely -assume that the authenticated entity is who it claims to be. + -The consequences can be catastrophic. - -In Dockerfiles, secrets hard-coded, secrets passed through as variables or +In Dockerfiles, hard-coded secrets and secrets passed through as variables or created at build-time will cause security risks. The secret information can be -exposed either via the container environment itself, the image metadata or the -build environment logs. +exposed either via the container environment, the image metadata, or the build +environment logs. -Docker Buildkit's secret mount options should be used when secrets have to be -accessed at build time. For run-time secrets, best practices would recommend -only setting them at runtime, for example with the `--env` option of the docker -run command. +=== What is the potential impact? -Note that files exposing the secrets should be securely stored and not exposed -to a large sphere. If possible, use a secret vault or another similar -component. For example, *Docker Swarm* provides a *secrets* service that can be -used to handle most confidential data. +include::../common/impact/rationale.adoc[] +include::../../../shared_content/secrets/impact/financial_loss.adoc[] -=== Noncompliant code example +include::../../../shared_content/secrets/impact/security_downgrade.adoc[] -The following code snippet demonstrates the creation of a file with a private -key and a public key, which are then stored in the metadata of the container. + -This is non-compliant, as the private key should not be exposed anywhere. +== How to fix it +Best practices recommend using a secret vault for all secrets that must be +accessed at container runtime. This will ensure the secret's security and +prevent any further unexpected disclosure. Depending on the development platform +and the leaked secret type, multiple solutions are currently available. + +For all secrets that must be accessed at image build time, it is recommended to +rely on Docker Buildkit's secret mount options. This will prevent secrets from +being disclosed in image's metadata and build logs. + +Additionally, investigations and remediation actions should be conducted to +ensure the current and future security of the infrastructure. + +include::../../../shared_content/secrets/fix/revoke.adoc[] + +include::../../../shared_content/secrets/fix/recent_use.adoc[] + +=== Code examples + +==== Noncompliant code example + +The following code sample generates a new SSH private key that will be stored in +the generated image. This key should be considered as compromised. Moreover, the +SSH key encryption passphrase is also hardcoded. [source,docker, diff-id=1, diff-type=noncompliant] ---- @@ -42,8 +53,8 @@ RUN ssh-keygen -N "passphrase" -t rsa -b 2048 -f /etc/ssh/rsa_key RUN /example.sh --ssh /etc/ssh/rsa_key ---- -In the following sample, the code uses a seemingly-hidden password which is -actually leaked after the container is built. +The following code sample uses a seemingly hidden password which is actually +leaked in the image metadata after the build. [source,docker, diff-id=2, diff-type=noncompliant] ---- @@ -54,12 +65,9 @@ ARG PASSWORD RUN wget --user=guest --password="$PASSWORD" https://example.com ---- -=== Compliant solution +==== Compliant solution -For build-time secrets, use -https://docs.docker.com/engine/reference/builder/#run---mounttypesecret[Buildkit's secret mount type] instead: - -[source,docker, diff-id=1, diff-type=compliant] +[source,docker,diff-id=1,diff-type=compliant] ---- FROM example @@ -67,16 +75,19 @@ RUN --mount=type=secret,id=ssh,target=/etc/ssh/rsa_key \ /example.sh --ssh /etc/ssh/rsa_key ---- -[source,docker, diff-id=2, diff-type=compliant] +[source,docker,diff-id=2,diff-type=compliant] ---- FROM example -RUN --mount=type=secret,id=wget_passwd \ - wget --user=guest --password="$(cat /run/secrets/wget_passwd)" https://example.com +RUN --mount=type=secret,id=wget,target=/home/user/.wgetrc \ + wget --user=guest https://example.com ---- -For runtime secrets, leave the environment variables empty in the Dockerfile. -Then store the runtime secrets in an +For runtime secrets, best practices recommend relying on a vault service to +pass secret information to the containers. Docker environment provides Swarm +services that implement such a feature. + +If such an option can not be considered, store the runtime secrets in an https://docs.docker.com/compose/env-file/[environment file] such as `.env` and then start the container with the https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file[`--env-file`] argument: @@ -86,13 +97,20 @@ https://docs.docker.com/engine/reference/commandline/run/#set-environment-variab docker run --env-file .env myImage ---- +It is then important to ensure that the environment files are securely stored +and generated. + == Resources -* https://docs.docker.com/engine/reference/builder/#run---mounttypesecret[Dockerfile reference] - RUN command secrets mount points -* https://docs.docker.com/engine/swarm/secrets/[Docker documentation] - Manage sensitive data with Docker secrets -* https://cwe.mitre.org/data/definitions/522.html[MITRE, CWE-522] - Insufficiently Protected Credentials -* https://cwe.mitre.org/data/definitions/798.html[MITRE, CWE-798] - Use of Hard-coded Credentials +include::../common/resources/documentation.adoc[] +* Docker Documentation - https://docs.docker.com/engine/swarm/secrets/[Manage sensitive data with Docker secrets] +* Docker Documentation - https://docs.docker.com/engine/reference/builder/#run---mounttypesecret[RUN command secrets mount points] + +=== Standards + +* CWE - https://cwe.mitre.org/data/definitions/522.html[CWE-522 - Insufficiently Protected Credentials] +* CWE - https://cwe.mitre.org/data/definitions/798.html[CWE-798 - Use of Hard-coded Credentials] ifdef::env-github,rspecator-view[] ''' @@ -115,5 +133,3 @@ For secret generation: ''' endif::env-github,rspecator-view[] - - diff --git a/rules/S6437/java/rule.adoc b/rules/S6437/java/rule.adoc index c04a429bc2..a55b1c2c9e 100644 --- a/rules/S6437/java/rule.adoc +++ b/rules/S6437/java/rule.adoc @@ -1,10 +1,32 @@ +include::../../../shared_content/secrets/description.adoc[] + == Why is this an issue? -include::../description.adoc[] +include::../../../shared_content/secrets/rationale.adoc[] -=== Noncompliant code example +=== What is the potential impact? -[source,java] +include::../common/impact/rationale.adoc[] + +include::../../../shared_content/secrets/impact/financial_loss.adoc[] + +include::../../../shared_content/secrets/impact/security_downgrade.adoc[] + +== How to fix it + +include::../../../shared_content/secrets/fix/revoke.adoc[] + +include::../../../shared_content/secrets/fix/recent_use.adoc[] + +include::../../../shared_content/secrets/fix/vault.adoc[] + +=== Code examples + +include::../common/fix/code-rationale.adoc[] + +==== Noncompliant code example + +[source,java,diff-id=1,diff-type=noncompliant] ---- import org.h2.security.SHA256; @@ -14,62 +36,25 @@ byte[] key = inputString.getBytes(); SHA256.getHMAC(key, message); // Noncompliant ---- -=== Compliant solution +==== Compliant solution -Using https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/secretsmanager[AWS Secrets Manager]: - -[source,java] +[source,java,diff-id=1,diff-type=compliant] ---- -import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; -import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; -import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; import org.h2.security.SHA256; -public static void doSomething(SecretsManagerClient secretsClient, String secretName) { - GetSecretValueRequest valueRequest = GetSecretValueRequest.builder() - .secretId(secretName) - .build(); +String inputString = System.getenv("SECRET"); +byte[] key = inputString.getBytes(); - GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest); - String secret = valueResponse.secretString(); - - byte[] key = secret.getBytes(); - SHA256.getHMAC(key, message); -} ----- - -Using https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-java?tabs=azure-cli[Azure Key Vault Secret]: - -[source,java] ----- -import com.azure.identity.DefaultAzureCredentialBuilder; -import com.azure.security.keyvault.secrets.SecretClient; -import com.azure.security.keyvault.secrets.SecretClientBuilder; -import com.azure.security.keyvault.secrets.models.KeyVaultSecret; -import org.h2.security.SHA256; - -public static void doSomething(SecretClient secretClient, String secretName) { - KeyVaultSecret retrievedSecret = secretClient.getSecret(secretName); - String secret = retrievedSecret.getValue(); - - byte[] key = secret.getBytes(); - SHA256.getHMAC(key, message); -} +SHA256.getHMAC(key, message); // Noncompliant ---- +include::../common/fix/how-it-works.adoc[] == Resources -* https://aws.amazon.com/fr/secrets-manager/[AWS] - Secret Manager -* https://azure.microsoft.com/fr-fr/services/key-vault/[Azure] - Key Vault -* https://cloud.google.com/secret-manager[GCP] - Secret Manager -* https://www.vaultproject.io/[Hashicorp Vault] - Secret Management -* https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/[OWASP Top 10 2021 Category A7] - Identification and Authentication Failures -* https://www.owasp.org/index.php/Top_10-2017_A2-Broken_Authentication[OWASP Top 10 2017 Category A2] - Broken Authentication -* https://cwe.mitre.org/data/definitions/798.html[MITRE, CWE-798] - Use of Hard-coded Credentials -* https://cwe.mitre.org/data/definitions/259.html[MITRE, CWE-259] - Use of Hard-coded Password -* https://wiki.sei.cmu.edu/confluence/x/OjdGBQ[CERT, MSC03-J.] - Never hard code sensitive information +include::../common/resources/documentation.adoc[] +include::../common/resources/standards.adoc[] ifdef::env-github,rspecator-view[] ''' @@ -86,5 +71,3 @@ Highlight the credential use and its initialization. ''' endif::env-github,rspecator-view[] - - diff --git a/rules/S6437/php/rule.adoc b/rules/S6437/php/rule.adoc index 2d46875907..72dc4da04c 100644 --- a/rules/S6437/php/rule.adoc +++ b/rules/S6437/php/rule.adoc @@ -1,56 +1,63 @@ +include::../../../shared_content/secrets/description.adoc[] + == Why is this an issue? -include::../description.adoc[] +include::../../../shared_content/secrets/rationale.adoc[] -=== Noncompliant code example +=== What is the potential impact? -[source,php] +include::../common/impact/rationale.adoc[] + +include::../../../shared_content/secrets/impact/financial_loss.adoc[] + +include::../../../shared_content/secrets/impact/security_downgrade.adoc[] + +== How to fix it + +include::../../../shared_content/secrets/fix/revoke.adoc[] + +include::../../../shared_content/secrets/fix/recent_use.adoc[] + +include::../../../shared_content/secrets/fix/vault.adoc[] + +=== Code examples + +include::../common/fix/code-rationale.adoc[] + +==== Noncompliant code example + +[source,php,diff-id=1,diff-type=noncompliant] ---- use Defuse\Crypto\KeyOrPassword; function createKey() { - $password = "example"; - return KeyOrPassword::createFromPassword($password); // Noncompliant + $password = "3xAmpl3"; // Noncompliant + return KeyOrPassword::createFromPassword($password); } ---- -=== Compliant solution +==== Compliant solution -Modern web frameworks tend to provide a secure way to pass passwords and secrets -to the code. For example, in Symfony you can use https://symfony.com/doc/current/configuration/secrets.html[vaults] -to store your secrets. The secret values are referenced in the same way as -environment variables, so you can easily access them through https://symfony.com/doc/current/configuration.html#configuration-parameters[configuration parameters]. - -[source,php] +[source,php,diff-id=1,diff-type=compliant] ---- use Defuse\Crypto\KeyOrPassword; -class PasswordService -{ - private string $password; - - public function setPassword(string $password): void - { - $this->password = $password; - } - - public function createKey(): KeyOrPassword - { - return KeyOrPassword::createFromPassword($this->password); - } +function createKey() { + $password = $_ENV["SECRET"] + return KeyOrPassword::createFromPassword($password); } ---- +include::../common/fix/how-it-works.adoc[] + == Resources -* https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/[OWASP Top 10 2021 Category A7] - Identification and Authentication Failures -* https://www.owasp.org/index.php/Top_10-2017_A2-Broken_Authentication[OWASP Top 10 2017 Category A2] - Broken Authentication -* https://cwe.mitre.org/data/definitions/798.html[MITRE, CWE-798] - Use of Hard-coded Credentials -* https://cwe.mitre.org/data/definitions/259.html[MITRE, CWE-259] - Use of Hard-coded Password -* https://wiki.sei.cmu.edu/confluence/x/OjdGBQ[CERT, MSC03-J.] - Never hard code sensitive information -* https://symfony.com/doc/current/configuration/env_var_processors.html[Symfony] - Environment Variable Processors -* https://symfony.com/doc/current/configuration.html#configuration-parameters[Symfony] - Configuring Symfony -* https://symfony.com/doc/current/configuration/secrets.html[Symfony] - How to Keep Sensitive Information Secret +include::../common/resources/documentation.adoc[] + +* Symfony - https://symfony.com/doc/current/configuration/secrets.html[How to +Keep Sensitive Information Secret] + +include::../common/resources/standards.adoc[] ifdef::env-github,rspecator-view[] ''' @@ -67,5 +74,3 @@ Highlight the credential use and its initialization. ''' endif::env-github,rspecator-view[] - - diff --git a/rules/S6437/python/rule.adoc b/rules/S6437/python/rule.adoc index dff1f421b3..fdf2a4ac97 100644 --- a/rules/S6437/python/rule.adoc +++ b/rules/S6437/python/rule.adoc @@ -1,10 +1,32 @@ +include::../../../shared_content/secrets/description.adoc[] + == Why is this an issue? -include::../description.adoc[] +include::../../../shared_content/secrets/rationale.adoc[] -=== Noncompliant code example +=== What is the potential impact? -[source,python] +include::../common/impact/rationale.adoc[] + +include::../../../shared_content/secrets/impact/financial_loss.adoc[] + +include::../../../shared_content/secrets/impact/security_downgrade.adoc[] + +== How to fix it + +include::../../../shared_content/secrets/fix/revoke.adoc[] + +include::../../../shared_content/secrets/fix/recent_use.adoc[] + +include::../../../shared_content/secrets/fix/vault.adoc[] + +=== Code examples + +include::../common/fix/code-rationale.adoc[] + +==== Noncompliant code example + +[source,python,diff-id=1,diff-type=noncompliant] ---- from requests_oauthlib.oauth2_session import OAuth2Session @@ -18,27 +40,15 @@ oauth = OAuth2Session( token = oauth.fetch_token( 'https://api.example.com/o/oauth2/token', client_secret='example_Password') # Noncompliant - -data = oauth.get('https://www.api.example.com/oauth2/v1/exampledata') ---- -=== Compliant solution +==== Compliant solution -Using https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/secretsmanager[AWS Secrets Manager]: - -[source,python] +[source,python,diff-id=1,diff-type=compliant] ---- -import boto3 +from os import environ from requests_oauthlib.oauth2_session import OAuth2Session -def get_client_secret(): - - session = boto3.session.Session() - client = session.client(service_name='secretsmanager', region_name='eu-west-1') - - return client.get_secret_value(SecretId='example_oauth_secret_id') - -client_secret = get_client_secret() scope = ['https://www.api.example.com/auth/example.data'] oauth = OAuth2Session( @@ -46,54 +56,20 @@ oauth = OAuth2Session( redirect_uri='https://callback.example.com/uri', scope=scope) -token = oauth.fetch_token( - 'https://api.example.com/o/oauth2/token', - client_secret=client_secret) - -data = oauth.get('https://www.api.example.com/oauth2/v1/exampledata') ----- - -Using https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-java?tabs=azure-cli[Azure Key Vault Secret]: - -[source,python] ----- -from azure.keyvault.secrets import SecretClient -from azure.identity import DefaultAzureCredential - -def get_client_secret(): - vault_uri = "https://example.vault.azure.net" - credential = DefaultAzureCredential() - client = SecretClient(vault_url=vault_uri, credential=credential) - - return client.get_secret('example_oauth_secret_name') - -client_secret = get_client_secret() -scope = ['https://www.api.example.com/auth/example.data'] - -oauth = OAuth2Session( - 'example_client_id', - redirect_uri='https://callback.example.com/uri', - scope=scope) +password = environ.get('OAUTH_SECRET') token = oauth.fetch_token( 'https://api.example.com/o/oauth2/token', - client_secret=client_secret) - -data = oauth.get('https://www.api.example.com/oauth2/v1/exampledata') - + client_secret=password) ---- +include::../common/fix/how-it-works.adoc[] + == Resources -* https://aws.amazon.com/fr/secrets-manager/[AWS] - Secret Manager -* https://azure.microsoft.com/fr-fr/services/key-vault/[Azure] - Key Vault -* https://cloud.google.com/secret-manager[GCP] - Secret Manager -* https://www.vaultproject.io/[Hashicorp Vault] - Secret Management -* https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/[OWASP Top 10 2021 Category A7] - Identification and Authentication Failures -* https://www.owasp.org/index.php/Top_10-2017_A2-Broken_Authentication[OWASP Top 10 2017 Category A2] - Broken Authentication -* https://cwe.mitre.org/data/definitions/798.html[MITRE, CWE-798] - Use of Hard-coded Credentials -* https://cwe.mitre.org/data/definitions/259.html[MITRE, CWE-259] - Use of Hard-coded Password -* https://wiki.sei.cmu.edu/confluence/x/OjdGBQ[CERT, MSC03-J.] - Never hard code sensitive information +include::../common/resources/documentation.adoc[] + +include::../common/resources/standards.adoc[] ifdef::env-github,rspecator-view[] ''' @@ -110,5 +86,3 @@ Highlight the credential use and its initialization. ''' endif::env-github,rspecator-view[] - -