Stop Storing Secrets in Your MuleSoft Source Code
Most MuleSoft developers asked how they protect credentials in CloudHub will give the same answer: Secure Configuration Properties. Encrypt your credentials using the secure properties tool, wrap them in a .yaml file in src/main/resources, reference the decryption key as a runtime property, and deploy.
It solves the plaintext credential problem on the surface. What sits underneath is the real issue.
Encrypted values are compiled directly into your deployable .jar file, and the decryption key that unlocks all of them must travel alongside the application to be of any use. That key has to come from somewhere, and more often than it should, the answer is to hardcode it in source code. It is a pattern seen most frequently in less experienced teams, but not exclusive to them: professional MuleSoft consultancies have been found doing it too.
OWASP's Secrets Management Cheat Sheet states that secrets must never be stored in source code. The Cryptographic Storage Cheat Sheet is equally explicit: do not hard-code keys into the application source code, and do not check keys into version control systems. CloudHub provides the platform features to do this properly. This article covers all of them.
Scale of the Problem
Hardcoded secrets are not a niche concern affecting a few careless developers.
GitGuardian's State of Secrets Sprawl 2026 report found 29 million new hardcoded secrets pushed to public GitHub repositories in 2025 alone, a 34% increase year-over-year and the largest single-year jump ever recorded. Over five years of tracking, leaked secrets have grown by 152%. GitHub's own data reported more than 39 million secret leaks across the platform in 2024.
Stolen credentials appear in 31% of all breaches, and the average time to detect and contain a credential-based breach is 292 days.
Consequences are not hypothetical. A GitHub token exposed in source code gave attackers access to the New York Times codebase in January 2024, leaking 270 GB of data. In March 2025, the tj-actions/changed-files GitHub Action was compromised, extracting secrets from CI/CD runners across more than 23,000 repositories. What most of these incidents share: credentials existed somewhere they should not have, for longer than anyone realised.
Five Risks Worth Naming
Putting the Key in a Global Property
Most direct failure mode. A team that understands the need to pass the decryption key at runtime reaches for the nearest available mechanism, the Mule <global-property> element. A configuration file like this is committed to source control every day:
<global-property name="encryption.key" value="mySecretEncryptionKey123" />
<secure-properties:config key="${encryption.key}"
file="config.yaml" name="config">
<secure-properties:encrypt algorithm="AES" mode="CBC"/>
</secure-properties:config>
Both the ciphertext and the key are now in the repository. Anyone with read access can decrypt every value immediately. Encryption with a publicly accessible key provides no security whatsoever.
A developer might hardcode the key during development intending to replace it before deployment. When the pull request gets merged and the application gets promoted, the key travels to production inside the repository history. Even if the property is later removed, the original commit containing the key remains in the git object database and is accessible via its SHA unless history is deliberately rewritten.
Symmetric Encryption and a Structural Problem
MuleSoft Secure Configuration Properties uses symmetric encryption: the same key encrypts and decrypts. IBM's analysis of symmetric encryption identifies its primary practical weakness: both parties must share the same key, and sharing requires a secure channel. In the MuleSoft context, that channel is the deployment process itself, which is exactly the thing you are trying to secure.
Several properties compound the risk specifically:
Single point of failure. One compromised key exposes everything encrypted with it. Every secret in every property file encrypted with the same key is exposed simultaneously.
No embedded metadata. Symmetric keys carry no inherent expiry dates or access control lists. Rotation means re-encrypting every value, rebuilding the .jar, and redeploying across every environment.
Blowfish is a legacy algorithm. MuleSoft documentation examples frequently show Blowfish, and many real-world implementations still use it. Blowfish was designed in 1993 and has a 64-bit block size, making it vulnerable to birthday attacks. Use AES with CBC mode and a 256-bit key for any new implementation.
OWASP recommends using hardware security modules or cloud key vaults to store symmetric keys. CloudHub's protected properties mechanism and Anypoint Secrets Manager are exactly this.
What Agentic AI Sees, It Learns
Agentic coding tools such as GitHub Copilot coding agent, Claude Code, and Cursor can now read entire codebases, create branches, write commit messages, and open pull requests with minimal human involvement.
These agents learn the patterns they observe. If the codebase shows encrypted property values committed to YAML files with decryption keys in global properties, an agent tasked with building a new integration will replicate that pattern.
GitGuardian found that AI service credential leaks reached 1.27 million in 2025, up 81% in a single year. A Stanford research team found that developers using AI assistants produced code with more security weaknesses, and were simultaneously more confident that their code was secure.
Custom AI instruction files stored in the repository make this worse. Pillar Security identified that poisoned rule files can silently instruct AI agents to introduce insecure patterns that survive project forks. If those instruction files normalise committing encrypted property files, every subsequent agent session on the team inherits that behaviour.
Pasting Secrets Into a Browser
MuleSoft's Secure Properties Tool is a command-line JAR file, but many developers instead reach for a web-based UI, such as the one hosted at https://secure-properties-api.us-e1.cloudhub.io, to generate encrypted values by pasting their secrets into a browser form.
A credential pasted into a web form is visible to any JavaScript executing on that page. Third-party scripts, analytics tags, and embedded widgets can all read form field values. Browser extensions can too. Research published in 2025 demonstrated DOM-based clickjacking attacks capable of stealing credentials from six major password managers, including LastPass and Bitwarden, by reading fields the user could not see.
Browser history, clipboard managers, developer tools, and corporate TLS inspection proxies add further surfaces where a pasted secret can appear in plaintext.
Unencrypted First, Committed Second
Using Secure Configuration Properties requires writing the plaintext value, running the encryption tool, replacing the plaintext with the ciphertext, and then committing. That sequence has a gap at every stage.
A developer might write the plaintext value as a placeholder, get interrupted, and push with the secret exposed. Feature branches are a common vector: files committed unencrypted intending to clean up before merging, only for the branch to be archived rather than deleted.
Once a secret has been pushed, it exists in the repository's history even if the file is subsequently deleted or overwritten. Making the repository private does not remove it. Rewriting history with tools like BFG Repo-Cleaner is possible, but it is a significant undertaking, and the credential must be treated as compromised regardless. GitGuardian found that 64% of valid secrets from 2022 are still active and exploitable in 2026.
Shared Keys and the Rotation Blind Spot
A pair of further risks are specific to how MuleSoft implementations get built in practice.
Reusing the same key across applications multiplies the blast radius. It is common for teams to settle on a key value that gets carried from project to project as a default, appearing in application templates and developer onboarding guides. Every application encrypted with that key is compromised the moment the key is. When a consultancy firm carries the same key across multiple client engagements, a single key disclosure potentially exposes every client simultaneously, without any individual client being able to assess their exposure or even know it has happened.
Security teams rarely know the encryption key exists. Credentials that Secure Configuration Properties protects appear in password managers, access reviews, and offboarding checklists. The encryption key does not. It is not a password to any external system. It does not grant access to anything a security team can see. It is not included in staff offboarding processes because nobody responsible for offboarding knows to ask.
Credentials protected by the key may be rotated quarterly. It is common for the key itself to have never been rotated at all.
Detection and Remediation
Secret Scanning Across Platforms
GitHub provides Secret Protection with retroactive history scanning and push protection that blocks commits containing secrets at the moment of the push attempt. Available as a standalone product at $19 per active committer per month, and free for public repositories.
GitLab provides built-in secret detection as part of its security scanning suite, supporting both pipeline scanning and push rules.
Bitbucket offers secret scanning for both Cloud and Data Center.
MuleSoft Secure Configuration Properties values appear in YAML files in three forms:
key: ![encryptedValue]
key: '![encryptedValue]'
key: "![encryptedValue]"
A custom secret scanning pattern targeting this syntax can be defined on all three platforms, flagging any committed file that contains encrypted property values and prompting developers to use safely hidden or protected properties instead. GitHub, GitLab, and Bitbucket all support custom patterns at the organisation level.
Rewriting History
BFG Repo-Cleaner by Roberto Tyley is the standard tool for rewriting git history to remove sensitive content. Ensure your HEAD commit is already clean, then:
git clone --mirror git@github.com:your-org/your-repo.git
java -jar bfg.jar --replace-text passwords.txt your-repo.git
cd your-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --force --all
git push --force --tags
Every team member must then discard their local clone and re-clone. A collaborator who runs git pull and git push from a pre-rewrite clone will reintroduce the secret.
GitHub maintains cached views of pull request commits that persist after a force push. Contact GitHub Support to request removal, or wait approximately 24 hours for cache expiry. Forks continue to hold the old commits independently.
GitLab has its own documented process for removing sensitive data, including server-side housekeeping tasks after the local rewrite.
Bitbucket supports history removal through local rewriting combined with Atlassian Support assistance for cached data.
Always rotate the credential before beginning history cleanup, not after. Cleanup removes future access; rotation neutralises current exposure.
Safely Hidden Properties on CloudHub 1.0
Safely Hidden Application Properties are an entirely separate feature from Secure Configuration Properties. Values are supplied through Runtime Manager at deployment time and never become part of your build artifact.
Setup requires three steps:
1. Declare the property names in mule-artifact.json. List the property names, not their values:
{
"minMuleVersion": "4.6",
"secureProperties": [
"db.password",
"api.client-secret",
"encryption.key"
]
}
2. Reference properties in your Mule XML as normal using ${db.password}.
3. Enter the actual values in the Properties tab when deploying through Runtime Manager. CloudHub masks them as you type. Once committed, the values are gone from the UI permanently and cannot be retrieved by any user, including MuleSoft staff.
When promoting an application between environments, safely hidden property values are not copied. Only property names move. Supply values fresh in each target environment: this prevents production credentials from landing in a sandbox.
Recommended by LinkedIn
Protected Properties on CloudHub 2.0
Protected Properties on CloudHub 2.0 simplify this further. Pre-declaring property names in mule-artifact.json is not required.
In the Properties tab, switch to Table view, enter the property name, enter the value, click Protect, confirm, then apply changes. Once protected, the value is encrypted in Anypoint Security secrets manager and cannot be retrieved by any user. Like CloudHub 1.0, values are not carried between environments during promotion.
Deployments managed programmatically can set protected properties through the Anypoint Runtime Manager REST API, which the CloudHub console itself uses under the hood.
Anypoint Security Secrets Manager: TLS and Certificates
Anypoint Security Secrets Manager is a dedicated vault for TLS contexts, keystores, truststores, and certificates.
Secrets are stored in secret groups, vaults scoped to an environment and business group. API Manager and Flex Gateway reference a secret by its identifier and the platform resolves it on their behalf. No certificate content or private key ever passes through your code, your pipeline, or your IDE.
Upload your TLS keystore and certificate to a secret group, create a TLS context, and reference that context in API Manager. When a certificate needs rotating, replace it in Secrets Manager and redeploy the affected API instances. No code change, no rebuild required.
Note: Secrets Manager's scope is TLS, API proxies, and platform infrastructure. Application-level credentials such as database passwords belong in safely hidden or protected properties.
External Secrets Managers: AWS and Azure
Teams whose infrastructure already sits on AWS or Azure can use MuleSoft native connectors that pull secrets from external vaults at application startup. Secrets never appear in a properties file, never travel through a build pipeline, and never live in source control in any form.
Amazon Secrets Manager Properties Provider
Amazon Secrets Manager Properties Provider retrieves secrets from AWS Secrets Manager before the Mule application starts. Property references use the aws-secrets:: prefix:
<s3:connection
accessKey="${aws-secrets::accessKey}"
secretKey="${aws-secrets::secretKey}" />
Authentication can use the AWS default provider chain, meaning the CloudHub worker authenticates using an IAM role with no static credentials stored anywhere in the application. When a credential needs rotating, update it in AWS Secrets Manager. No code change, no new JAR, no pipeline run. Operations teams with the appropriate IAM permissions can perform the rotation entirely independently of the development team.
Azure Key Vault Connector
Azure Key Vault Connector provides the same capability for Azure infrastructure. When configured with Azure Default Credentials, the connector authenticates using the environment's managed identity. Azure RBAC then controls which CloudHub environments or application identities can access specific secrets in a vault.
Self-Signed Certificates for Local HTTPS Development
Running a MuleSoft API locally over HTTPS requires a TLS certificate and keystore. Committing a self-signed keystore to the source repository means every developer can run a HTTPS instance locally without any setup. It also means a private key is now in source control and will accumulate in git history indefinitely.
Generate the certificate at build time instead. A pair of Maven plugins handles this.
First, build-helper-maven-plugin resolves the current machine's hostname:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>init-build-properties</id>
<goals><goal>hostname</goal></goals>
<configuration>
<hostnameProperty>hostname</hostnameProperty>
</configuration>
</execution>
</executions>
</plugin>
Second, keytool-maven-plugin generates a fresh key pair using that hostname as both the Common Name and a Subject Alternative Name, so the certificate is valid for the local HTTPS instance:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>keytool-maven-plugin</artifactId>
<executions><execution>
<goals><goal>generateKeyPair</goal></goals>
<phase>generate-resources</phase>
</execution></executions>
<configuration>
<keyalg>RSA</keyalg>
<validity>1</validity>
<dname>cn=${hostname}, ou=Dev, o=DevOps, c=US</dname>
<ext>SAN="DNS:${hostname},IP:127.0.0.1"</ext>
<alias>${tls.keystore.alias}</alias>
<keystore>${keytool.path}</keystore>
<storetype>${tls.keystore.type}</storetype>
<storepass>${tls.keystore.password}</storepass>
</configuration>
</plugin>
Adding the output path to your .gitignore solves the problem at the source. Every machine generates its own certificate, no private key ever enters the repository, and git history stays clean.
Automating Secrets in Your CI/CD Pipeline
Anypoint CLI
Anypoint CLI supports a --secureProperty flag on both the runtime-mgr:cloudhub-application:deploy and runtime-mgr:cloudhub-application:modify commands:
anypoint-cli runtime-mgr:cloudhub-application:deploy \
my-application \
my-application.jar \
--secureProperty "db.password:${DB_PASSWORD}" \
--secureProperty "api.client-secret:${API_CLIENT_SECRET}"
Values are supplied at runtime by your CI/CD platform's secrets store, never hardcoded.
Mule Maven Plugin with Connected App Credentials
When deploying via the Mule Maven Plugin to CloudHub 2.0, authentication uses a Connected App's client_id and client_secret stored in your CI/CD secrets store.
Maven also needs access to Anypoint Exchange to resolve MuleSoft dependencies during the build phase. Both the build and deployment phases require a settings.xml that authenticates using the Connected App's credentials in the format ~~~Client~~~ as the username and clientID~?~clientSecret as the password.
Both settings.xml files must be generated dynamically by the CI/CD pipeline from environment variables and must never be written to permanent storage on disk. In GitHub Actions, one file is generated for the build phase and a separate one for deployment:
- name: Generate build settings
run: |
cat > /tmp/build-settings.xml << EOF
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${{ secrets.CONNECTED_APP_CLIENT_ID }}~?~${{ secrets.CONNECTED_APP_CLIENT_SECRET }}</password>
</server>
</servers>
</settings>
EOF
- name: Build
run: mvn clean package -s /tmp/build-settings.xml
- name: Generate deploy settings
run: |
cat > /tmp/deploy-settings.xml << EOF
<settings>
<servers>
<server>
<id>anypoint-exchange-v3</id>
<username>~~~Client~~~</username>
<password>${{ secrets.CONNECTED_APP_CLIENT_ID }}~?~${{ secrets.CONNECTED_APP_CLIENT_SECRET }}</password>
</server>
</servers>
</settings>
EOF
- name: Deploy
run: |
mvn deploy -DmuleDeploy -s /tmp/deploy-settings.xml \
-DconnectedAppClientId=${{ secrets.CONNECTED_APP_CLIENT_ID }} \
-DconnectedAppClientSecret=${{ secrets.CONNECTED_APP_CLIENT_SECRET }} \
-Denvironment=Production \
-DtargetName=Cloudhub-EU-West-1 \
-DappName=my-api
Writing to /tmp scopes the credentials to the pipeline run. Never commit a settings.xml containing credentials. Never use Maven's --encrypt-master-password for pipeline credentials: that mechanism writes an encrypted file to disk, which contradicts the principle that secrets should not exist on disk at all.
GitHub Actions, Jenkins, and Azure Pipelines
GitHub Actions stores secrets as encrypted environment variables. Reference them in your workflow with ${{ secrets.DB_PASSWORD }} and GitHub automatically masks the value from logs. Scope secrets to deployment environments and configure protection rules requiring manual approval before production access.
Jenkins should use vault-backed credential providers rather than storing secrets on the controller disk. The AWS Secrets Manager Credentials Provider plugin reads credentials directly from AWS Secrets Manager at runtime. Tag each secret with jenkins:credentials:type=string, grant the Jenkins IAM role secretsmanager:GetSecretValue and secretsmanager:ListSecrets, and the secret becomes available by name in your Jenkinsfile with no disk storage involved:
withCredentials([string(credentialsId: 'db-password', variable: 'DB_PASSWORD')]) {
sh 'mvn deploy -DdbPassword=$DB_PASSWORD'
}
On Azure, the Azure Key Vault plugin fetches secrets at runtime using the withAzureKeyvault step and injects them as environment variables. In both cases, the secret exists only in memory for the duration of the step that needs it and is never written to the Jenkins controller or agent disk.
Azure Pipelines supports secret variables at the pipeline and variable group level, masked in logs. Linking a variable group to Azure Key Vault provides centralised rotation, audit logging, and RBAC access control.
When Secure Configuration Properties Is All You Have
There is an override mechanism that MuleSoft published in a support knowledge article in July 2025 that most teams do not know about: you can update an encrypted property value in Runtime Manager without rebuilding the application at all.
In Table view, enter the property name with the secure:: prefix and the new plaintext value in the value field:
secure::db.password | newPlaintextValue
In Text view, escape the colons:
secure\:\:db.password=newPlaintextValue
Omitting the prefix silently creates a new non-secure property instead of updating the existing one. Rotation appears to have succeeded when it has not.
This gives operations teams the ability to rotate credentials without involving development. However, the property name must already be listed in secureProperties in mule-artifact.json, so development setup is still a prerequisite.
Key Takeaways
Hardcoded secrets are a systemic, growing problem. 29 million new secrets were pushed to GitHub in 2025 alone, up 34% year-over-year. Stolen credentials are involved in 31% of all breaches and take an average of 292 days to detect and contain.
Never put the encryption key in a global property. If the key is in source control alongside the ciphertext, the encryption provides no protection.
Never reuse the same encryption key across applications or clients. A single key shared across multiple projects expands the blast radius of any exposure to every application that ever used it.
Track and rotate the encryption key like any other credential. Security teams are often unaware it exists. Include it in staff offboarding processes.
Symmetric encryption is structurally problematic for this use case. A single compromised key exposes all data encrypted with it, rotation requires rebuilding and redeploying the artifact, and symmetric keys carry no built-in metadata for expiry or access control.
Agentic AI tools amplify whatever patterns they find in your repository. Review your AI instruction files to ensure they do not normalise insecure defaults.
Pasting secrets into a browser form is riskier than it appears. If you must use Secure Configuration Properties, use the command-line JAR locally.
Baking secrets into a compiled JAR prevents operations from acting alone. Safely hidden properties, protected properties, and external vault connectors all allow credential rotation without touching source code or triggering a build.
Overriding an encrypted property without a rebuild is possible. In Table view: secure::property.name. In Text view: secure\:\:property.name. Omitting the prefix silently creates a non-secure duplicate.
Safely Hidden Properties (CloudHub 1.0) and Protected Properties (CloudHub 2.0) are the right answer for most teams. CloudHub stores them encrypted and they cannot be retrieved by any user.
Anypoint Security Secrets Manager handles TLS certificates and keystores. Rotate them without touching code.
Never commit self-signed certificates or keystores to source control. Generate them at build time using build-helper-maven-plugin and keytool-maven-plugin.
Generate your settings.xml dynamically in your CI/CD pipeline. Write it to /tmp from CI/CD secrets store variables, use it for the build and deployment steps, and discard it. Never commit it to source control and never write credentials to permanent disk storage.
Enable secret scanning on your repositories. GitHub, GitLab, and Bitbucket all support push protection and retroactive history scanning. Add a custom pattern targeting the MuleSoft ![...] encryption syntax to catch committed encrypted property values.
If secrets have already been committed, clean history and rotate immediately. Use BFG Repo-Cleaner. Require every team member to re-clone. Contact your platform (GitHub, GitLab, or Bitbucket) to remove cached data. Cleaning history does not substitute for revoking the credential.
Helpful insights on what should of be a no-brainer. Thanks, Beech Horn !
" OWASP® Foundation's Secrets Management Cheat Sheet states that secrets must never be stored in source code." 100% and always on point.
Great article and alert👏