Secret Management

edit Introduced: 7.0

Secret Management

Securely store and access sensitive data (credentials, API keys) using configurable secret providers. Keeps secrets out of code and config files.

Working Example Available A complete Docker-based example demonstrating all providers covered in this document — including LocalStack for local AWS mocking — is available at: github.com/lucee/lucee-docs/tree/master/examples/docker/secret-provider

SecretProviderGet()

Configuration

Secret providers are configured in .CFConfig.json under the secretProvider key. Each provider has a name (used when calling SecretProviderGet), a class, and optional custom properties.

Environment Variables Provider

Reads secrets directly from environment variables:

"secretProvider": {
  "env": {
    "class": "lucee.runtime.security.EnvVarSecretProvider",
    "custom": {
      "caseSensitive": false
    }
  }
}

File Provider

Reads secrets from a .json or .env file:

"secretProvider": {
  "json": {
    "class": "lucee.runtime.security.FileSecretProvider",
    "custom": {
      "type": "json",
      "file": "/path/to/secrets.json",
      "caseSensitive": false
    }
  }
}

AWS Secrets Manager Provider

Reads secrets from AWS Secrets Manager. Provided by the org.lucee:aws-sm library, loaded automatically via Maven:

"secretProvider": {
  "sm": {
    "class": "org.lucee.extension.aws.sm.AWSSecretManagerProvider",
    "maven": "org.lucee:aws-sm:1.0.0.6-RC",
    "custom": {
      "accessKeyId": "your-access-key-id",
      "secretKey": "your-secret-key",
      "region": "us-east-1",
      "jsonTraversal": true,
      "timeout": 5000
    }
  }
}

AWS Parameter Store Provider

Reads secrets from AWS Systems Manager Parameter Store. Also provided by the org.lucee:aws-sm library:

"secretProvider": {
  "ps": {
    "class": "org.lucee.extension.aws.ssm.AWSParameterStoreProvider",
    "maven": "org.lucee:aws-sm:1.0.0.6-RC",
    "custom": {
      "accessKeyId": "your-access-key-id",
      "secretKey": "your-secret-key",
      "region": "us-east-1",
      "jsonTraversal": true,
      "timeout": 5000
    }
  }
}

Combined Providers

The AndSecretProvider chains multiple providers together, checking each in order until a secret is found:

"secretProvider": {
  "many": {
    "class": "lucee.runtime.security.AndSecretProvider",
    "custom": {
      "providers": "env,json"
    }
  }
}

Full Example Configuration

A complete .CFConfig.json combining all built-in and AWS providers:

{
  "secretProvider": {
    "env": {
      "class": "lucee.runtime.security.EnvVarSecretProvider",
      "custom": {
        "caseSensitive": false
      }
    },
    "json": {
      "class": "lucee.runtime.security.FileSecretProvider",
      "custom": {
        "type": "json",
        "file": "/path/to/secrets.json",
        "caseSensitive": false
      }
    },
    "sm": {
      "class": "org.lucee.extension.aws.sm.AWSSecretManagerProvider",
      "maven": "org.lucee:aws-sm:1.0.0.6-RC",
      "custom": {
        "accessKeyId": "your-access-key-id",
        "secretKey": "your-secret-key",
        "region": "us-east-1",
        "jsonTraversal": true,
        "timeout": 5000
      }
    },
    "ps": {
      "class": "org.lucee.extension.aws.ssm.AWSParameterStoreProvider",
      "maven": "org.lucee:aws-sm:1.0.0.6-RC",
      "custom": {
        "accessKeyId": "your-access-key-id",
        "secretKey": "your-secret-key",
        "region": "us-east-1",
        "jsonTraversal": true,
        "timeout": 5000
      }
    }
  }
}

Supported Providers

Provider Class Source
Environment Variables lucee.runtime.security.EnvVarSecretProvider Lucee Core
File (JSON/ENV) lucee.runtime.security.FileSecretProvider Lucee Core
Combined/Chained lucee.runtime.security.AndSecretProvider Lucee Core
AWS Secrets Manager org.lucee.extension.aws.sm.AWSSecretManagerProvider aws-sm extension
AWS Parameter Store org.lucee.extension.aws.ssm.AWSParameterStoreProvider aws-sm extension

Provider Configuration Options

EnvVarSecretProvider

Option Description Default
caseSensitive Whether key lookups are case-sensitive true

FileSecretProvider

Option Description Default
type File format: json or env env
file Path to the secrets file (required)
caseSensitive Whether key lookups are case-sensitive true
refreshInterval How often to check for file changes (ms) 60000

AndSecretProvider

Option Description Default
providers Comma-separated list of provider names to check in order (required)

AWSSecretManagerProvider / AWSParameterStoreProvider

Option Description Default
accessKeyId AWS access key ID
secretKey AWS secret access key
region AWS region us-east-1
endpoint Custom endpoint URL (useful for local mocking with LocalStack)
jsonTraversal Enables dot-notation access into JSON secret values true
timeout Cache duration in milliseconds. 0 disables caching 0
checkEnviroment Whether to fall back to environment credentials true

Using Secrets in Your Application

Secrets are accessed via the SecretProviderGet() function.

// Get from a specific provider
apiKey = SecretProviderGet("API_KEY", "env");

// Get from any provider (first match wins) dbPassword = SecretProviderGet("DB_PASSWORD");

JSON Traversal

When a secret value is a JSON string and jsonTraversal is enabled, you can use dot notation to access nested keys directly:

// Returns the full JSON string: {"username":"admin","password":"supersecret"}
secret = SecretProviderGet("mysecret", "sm");

// Returns just "supersecret" password = SecretProviderGet("mysecret.password", "sm");

Storing References at Startup

Secrets return a lazy reference — the actual value is only resolved when used. This means you can safely store references at application startup and they will always reflect the current secret value, even after rotation:

// Application.cfc
component {
  function onApplicationStart() {
    application.secrets = {
      apiKey:   SecretProviderGet("API_KEY", "env"),
      dbHost:   SecretProviderGet("DB_HOST", "sm"),
      dbUser:   SecretProviderGet("DB_USER", "sm"),
      dbPass:   SecretProviderGet("DB_PASSWORD", "sm")
    };
  }
}

Local Development with LocalStack

For local development you can mock AWS Secrets Manager and Parameter Store using LocalStack. Configure the endpoint property to point to your LocalStack instance instead of real AWS:

"sm": {
  "class": "org.lucee.extension.aws.sm.AWSSecretManagerProvider",
  "maven": "org.lucee:aws-sm:1.0.0.6-RC",
  "custom": {
    "accessKeyId": "dummy",
    "secretKey": "dummy",
    "region": "us-east-1",
    "endpoint": "http://localstack:4566"
  }
}

Any non-empty string works for accessKeyId and secretKey when pointing at LocalStack.

Key Features

Lazy Resolution

SecretProviderGet returns a reference, not the actual value. The secret is resolved only when used as a simple value (string, number, boolean). This means:

  • Auto-updating: If a secret rotates in the provider, your app picks up the new value automatically.
  • Reduced memory exposure: Secret values aren't held in memory until actually needed.

Obfuscation

Secret values are automatically obfuscated in dump() output and debug logs, reducing accidental exposure.

Using External Classes via Maven

Since Lucee 6.2, external provider classes can be loaded directly from Maven by specifying the maven key:

"sm": {
  "class": "org.lucee.extension.aws.sm.AWSSecretManagerProvider",
  "maven": "org.lucee:aws-sm:1.0.0.6-RC",
  "custom": { ... }
}

Installing the AWS Extension

The org.lucee:aws-sm Maven jar is downloaded automatically by Lucee when first used — no manual installation required. However, pre-installing the extension means the jars are already available locally, avoiding any download at startup. This is recommended for production and air-gapped environments.

There are two ways to pre-install it in .CFConfig.json:

Via Maven (downloads from Maven repository)

"extensions": [
  {
    "id": "16953C9D-0A26-4283-904AD851B30506AF",
    "name": "AWS Secret Manager Extension",
    "version": "1.0.0.6-RC",
    "maven": "org.lucee:aws-sm-extension:1.0.0.6-RC"
  }
]

Via Local File (no download needed)

"extensions": [
  {
    "id": "16953C9D-0A26-4283-904AD851B30506AF",
    "name": "AWS Secret Manager Extension",
    "version": "1.0.0.6-RC",
    "source": "/path/to/aws-sm-extension-1.0.0.6-RC.lex"
  }
]

Use the source approach when you want fully offline/reproducible deployments — for example, by including the .lex file in your Docker image alongside your artifacts/ directory.

Creating Custom Secret Providers

You can implement your own provider as a CFML component using implementsJava:

// MySecretProvider.cfc
component implementsJava="lucee.runtime.secrets.SecretProvider" {

function init(struct config) { variables.config = config; return this; }
function getSecret(string key) { var q = queryExecute( "SELECT value FROM app_secrets WHERE key = :key", {key: key}, {datasource: config.datasource} ); return q.recordCount ? q.value : javaNull(); }
function hasSecret(string key) { var q = queryExecute( "SELECT COUNT(*) as cnt FROM app_secrets WHERE key = :key", {key: key}, {datasource: config.datasource} ); return q.cnt > 0; } }

Register it in .CFConfig.json:

"secretProvider": {
  "database": {
    "component": "path.to.MySecretProvider",
    "custom": {
      "datasource": "secretsDB"
    }
  }
}

Working Example

A complete working Docker-based example is available in the Lucee documentation repository:

github.com/lucee/lucee-docs/tree/master/examples/docker/secret-provider

It includes a Docker Compose setup with LocalStack mocking AWS Secrets Manager and Parameter Store, along with a test.cfm demonstrating all providers covered in this document.

Troubleshooting

Lucee logs every secret access at trace level in the application log, including the key, provider name, and stack trace (but never the value). Enable trace-level logging to audit secret usage.

See also