← All essays

Engineering · Aug 2026 · 5 min read

SSJS and external APIs: the token hygiene checklist.

HTTPGet, HTTPPost, WSProxy. Every place a bearer token can leak into rendered output. Eight patterns to check for, and how to design the wrapper so the token never touches string concatenation with untrusted data.

SSJS in Marketing Cloud can call external APIs. HTTPGet, HTTPPost, and the WSProxy wrapper for SOAP endpoints. Almost every non trivial Cloud Page or Script Activity we inherit does at least one of these.

Almost none of them handle the bearer token correctly. The token leaks in ways that are subtle and, when they happen, catastrophic. This is the checklist we run against every SSJS block that touches an external API.

Where tokens leak

A leak is any path by which the token reaches a place it should not be. Rendered HTML. A log entry. A Data Extension column. A response body returned to a user. Anywhere the token appears where someone other than the SSJS execution context can read it.

The tokens that matter are the ones with real authority: OAuth bearer tokens for external APIs, API keys for internal services, service account credentials for cloud providers. If the leaked value would let an attacker impersonate your service, it is worth this level of paranoia.

The eight patterns to check for

One: a debug Write call left in production. During development, developers Write the token to the response to confirm it was fetched. The Write survives the merge. The token appears in the rendered Cloud Page for every visitor. This is the most common cause of leaks we see.

Two: an error message that echoes the request headers. A Try/Catch block that logs the full request object on failure. The request object contains the Authorization header. The log entry contains the token. Anyone with access to the log has the token.

Three: a response body that contains the token, logged into a Data Extension. Some API responses echo the credential back for confirmation. The wrapper logs the raw response to a debug Data Extension. The token is now in a Data Extension column that anyone with read access to the DE can query.

Four: an SSJS block rendered as if it were AMPscript. The developer meant to put the script in a Script tag but pasted it into a Content Builder block. The block renders. The SSJS is not executed, it is emitted as text. The token appears in the rendered email.

Five: string concatenation with untrusted input. The wrapper builds a URL by concatenating the token with a query parameter that came from the request. The query parameter contains a URL redirect that pushes the entire URL, token included, to an attacker controlled endpoint. This one is subtle and it happens.

Six: an unbounded response body echoed to the client. A wrapper that fetches an external API and returns the raw response to the Cloud Page. If the response contains the token as an error diagnostic, the client now has it. This one bites when the upstream API changes its error format.

Seven: a stack trace on an unhandled exception. Marketing Cloud's default error handler for Cloud Pages sometimes renders a stack trace that includes the values of local variables. The token is a local variable. The stack trace is the visible output. Wrap everything in a top level Try/Catch that catches to a generic error message before the platform's default handler can format the raw trace.

Eight: the token committed to source control. The wrapper reads the token from a hardcoded string constant, which lives in the SSJS block, which lives in a repository. The repository is shared. The token is now in git history. Rotate it. This one seems obvious. It is still the second most common leak.

How to design the wrapper

The design that closes all eight patterns has three habits.

First: the token never touches string concatenation with anything else. It goes into the Authorization header via the request object API, never through URL construction, never through body construction. The wrapper takes the token as an argument and hands it directly to the HTTP call. Nothing between.

Second: the wrapper redacts the token from any log or debug output before it leaves the wrapper. The token comes in, gets used for exactly one HTTP call, and any object that references it is scrubbed before it can be logged. If the log line reads "Authorization: Bearer [REDACTED]" instead of the raw token, the leak surface shrinks to nothing.

Third: the wrapper always catches its own exceptions and returns a sanitised error. Never let the platform's default error handler see an exception whose local variables include the token. Catch at the wrapper boundary, format a generic error, log the details to a safe place, return the generic error to the caller.

One more habit: rotate on suspicion

If any of the eight patterns above ever fired in your codebase, even if you fixed the code, rotate the token. The window between the code shipping and you noticing is the window an attacker had to harvest it. Rotation is cheap. Assuming the leak is contained is not.

Every serious API vendor provides a way to rotate the credential without downtime. Use it. Do it the same day you find the leak, not the following sprint.

“Assume the token has leaked. Design the wrapper so leaking it is genuinely hard, and rotate the moment you suspect otherwise.”

Keep reading

Engineering · Aug 2026

AMPscript error handling: catching the silent failures.

Product · Jul 2026

Cloud Pages as internal tools, quietly.