Secrets Viewtool

Last Updated: Jul 1, 2024
documentation for the dotCMS Content Management System

The $dotsecrets Viewtool allows passing of a secret string or character array — such as a token, key, password, etc. — via Velocity, without including sensitive data in the VTL file.

The Secrets Viewtool debuted in release 23.07. Its toolbox.xml mapping is as follows:

    <tool>
        <key>dotsecrets</key>
        <scope>application</scope>
        <class>com.dotcms.rendering.velocity.viewtools.SecretTool</class>
    </tool>

General Security Notes

The handling of secrets in any context calls for care; misuse of the Secrets Viewtool can lead to exposure of sensitive data.

We recommend using the Secrets Viewtool in conjunction with a custom Scripting API endpoint or the Velocity Script Workflow Sub-Action, as these offer no opportunity for a secret to be unintentionally rendered.

When used within a Widget or Page, avoid printing $dotsecrets anywhere it has the potential to be rendered. One easy guideline for this use case is to use the viewtool only within the parentheses of a Velocity directive. For example:

#if($userInput == $dotsecrets[$userKey])
    Render this block.
#end

This ensures the secret will not appear in a user's browser session.

When using the Secrets Viewtool as part of an API call to an external application, it is important that the data retrieved from $dotsecrets be sent directly to that external target instead of back to a browser session. While many in many contexts it is appropriate to offload API calls to a client to spare server load, this pattern is not recommended in the context of $dotsecrets, as it virtually guarantees the secret's exposure. Instead, wrap the call in a Scripting API method; if the server handles the sensitive call and simply returns its results to the client, this presents no such risk.

Role Requirement

As an additional security measure, the Secrets Viewtool has a role requirement — the publisher must have the Scripting User system role. Likewise, if accessing through an API token, the token must have come from a user with this role.

If a user without this role publishes content containing calls to $dotsecrets any provided keys will return a value of null.

In the context of push publishing, the configured push-publishing user on the receiving server should likewise be equipped with the Scripting User role. This does not imply that any user who can push publish will transitively be able to use $dotsecrets on the receiving server; the process of saving a draft or publishing on the sending server will, in the case of a non-Scripting User, deactivate the viewtool before it is sent to the receiver.

Disabling Feature

To disable the Secrets Viewtool across the system, set the following environment variable:

DOT_SECRETS_SCRIPTING_ENABLED: 'false'

This variable is set to true by default.

Note: This is distinct from the DOT_ENABLE_SCRIPTING variable used by the Scripting User system role.

Usage

App Configuration

The Secrets Viewtool interfaces with a special app called Dot Velocity Secrets, which lives under System → Apps. All keys must be stored as Custom Properties under either a specific site or the system host. No site can access the secrets of another site, though secrets stored at the system host level are accessible from any site. As a general rule, it is better to make secrets as narrowly accessible as possible; accordingly, it is best to use system host secrets only in cases where one operation affects multiple sites.

The Dot Velocity Secrets App, seen with example values such as key "test1" and value "secret1".

Headless Configuration

The Dot Velocity Secrets App can be configured headlessly through a special /v1/apps/dotVelocitySecretApp REST API endpoint. To POST to this endpoint, use the relevant site identifier as a path parameter, and include a JSON payload of properties with the following schema:

{
    "property": {
        "hidden": true|false,
        "value": "string"
    }
}

There is a single reserved property called title, which provides a descriptive title for the Site entry within the app. Its hidden property cannot be changed from false.

All other properties are keys corresponding to a secret value.

Example of updating the dotCMS Demo Site:

curl -v -u admin@dotcms.com:admin -X POST https://demo.dotcms.com/api/v1/apps/dotVelocitySecretApp/48190c8c-42c4-46af-8d1a-0cd5db894797 -H "Content-Type: application/json" -d '{
    "title":{"hidden":false,"value":"dotCMS demo site secrets"},
    "test2":{"hidden":true,"value":"secret2"},
    "test1":{"hidden":false,"value":"secret1"}
    }'

Similarly, updating the System Host:

curl -v -u admin@dotcms.com:admin -X POST https://demo.dotcms.com/api/v1/apps/dotVelocitySecretApp/SYSTEM_HOST -H "Content-Type: application/json" -d '{
    "title":{"hidden":false,"value":"dotCMS demo site system secrets"},
    "sysSecret1":{"hidden":false,"value":"thebigsecret"}
    }'

Using either of those URLs with the GET HTTP method will simply return their properties. If you GET the endpoint without a path parameter, it returns a list of site entries within the app, but not their individual properties.

Velocity Commands

CommandResult
$dotsecrets.get("foo") or $dotsecrets.foo or $dotsecrets["foo"] or $dotsecrets[$foo]Four equivalent ways to retrieve the local site secret stored with key foo — in the last case after the declaration of #set($foo = "foo")
$dotsecrets.getSystemSecret("foo")Retrieves the system host secret stored with the key foo.
$dotsecrets.getCharArray("foo")Similar to .get("foo"), but returns the local site secret as an array of characters instead of a string.
$dotsecrets.getCharArraySystemSecret("foo")Similar to .getSystemSecret("foo"), but returns the system host secret as an array of characters instead of a string.

Examples

Passing an API Key or Token in Velocity

After storing the access token in the Dot Velocity Secret App under the key myKey, you can include it in the Authorization header of an API call as follows:

#set($queryString = "{ \"query\":\"hello world\" }")
#set($timeout = 3000)
#set($headers = {})
$!{headers.put("Authorization", "bearer ${dotsecrets.myKey}")}
$!{headers.put("Content-type" , "application/json")}
#set($results = $json.post("https://api.example.com/endpoint", $timeout, $headers, $queryString))

On this page

×

We Dig Feedback

Selected excerpt:

×