Security
SSJS security best practices — prevent injection, validate all inputs, protect against CSRF, secure tokens, and avoid data leakage.
CloudPages that process user input or call external APIs are security-sensitive. This page covers the most important SSJS security practices.
1. Validate All User Input
Never trust query strings, POST bodies, form fields, or cookies. Always validate before using.
var email = Platform.Request.GetFormField("email");
// Validate email format
if (!Platform.Function.IsEmailAddress(email)) {
Write(Stringify({ status: 400, statusMessage: "Bad Request", error: "Invalid email address" }));
return;
}
var id = Platform.Request.GetQueryStringParameter("id");
// Validate numeric ID
if (!id || !/^\d+$/.test(id)) {
Write(Stringify({ status: 400, statusMessage: "Bad Request", error: "Invalid id" }));
return;
}
id = parseInt(id, 10);
2. Never Inject Input into Platform.Function.TreatAsContent
Platform.Function.TreatAsContent() evaluates AMPscript. Passing user-controlled data to it creates a Server-Side Template Injection (SSTI) vulnerability.
// DANGEROUS — user can inject AMPscript
Platform.Function.TreatAsContent(userInput);
// SAFE — set via Variable, then use a fixed template
Variable.SetValue("@userInput", userInput);
Platform.Function.TreatAsContent("%%=v(@userInput)=%%"); // v() output-encodes the value
3. Protect API Tokens
Never hardcode tokens in SSJS source code. Store them in a DE or use SFMC Key Management.
// BAD — token visible in source/logs
var token = "Bearer sk-abc123secrettoken";
// GOOD — load from Config DE
var token = Platform.Function.Lookup("AppConfig", "value", "key", "apiToken");
// BETTER — load from encrypted field
var encryptedToken = Platform.Function.Lookup("AppConfig", "encryptedToken", "key", "apiToken");
function decryptSymmetric(encryptedString, algorithm, passwordKey, passwordValue,saltKey, saltValue, vectorKey, vectorValue) {
Platform.Variable.SetValue("@decrypt_string", encryptedString);
Platform.Variable.SetValue("@decrypt_algo",algorithm);
Platform.Variable.SetValue("@decrypt_pw",passwordValue || "");
Platform.Variable.SetValue("@decrypt_salt",saltValue || "");
Platform.Variable.SetValue("@decrypt_vector",vectorValue || "");
return Platform.Function.TreatAsContent("%%=DecryptSymmetric(@decrypt_string, @decrypt_algo, @null,@decrypt_pw, @null, @decrypt_salt, @null, @decrypt_vector)=%%");
}
var token = decryptSymmetric(encryptedToken, "AES", "myKey", "myIV");
4. CSRF Protection for Forms
CloudPages forms are publicly accessible. Without CSRF protection, any site can submit to your form.
// Generate CSRF token on page load (GET)
if (Platform.Request.Method === "GET") {
var csrfToken = Platform.Function.GUID();
Platform.Response.SetCookie("csrfToken", csrfToken, "", true);
// Output token in form
Write('<input type="hidden" name="csrf_token" value="' + csrfToken + '">');
}
// Validate on POST
if (Platform.Request.Method === "POST") {
var tokenFromCookie = Platform.Request.GetCookieValue("csrfToken");
var tokenFromForm = Platform.Request.GetFormField("csrf_token");
if (!tokenFromCookie || tokenFromCookie !== tokenFromForm) {
Write(Stringify({ status: 403, statusMessage: "Forbidden", error: "CSRF validation failed" }));
return;
}
// Process form...
}
5. Token-Based API Authentication
For JSON endpoints called by other services:
// Shared secret authentication
var receivedToken = Platform.Request.GetRequestHeader("X-API-Token");
var expectedToken = Platform.Function.Lookup("AppConfig", "value", "key", "apiSecret");
if (!receivedToken || receivedToken !== expectedToken) {
Write(Stringify({ status: 401, statusMessage: "Unauthorized", error: "Unauthorized" }));
return;
}
6. Output Encoding
Always HTML-encode output from user input to prevent XSS:
function htmlEncode(str) {
return (str + "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// DANGEROUS
Write("<div>Hello, " + userName + "</div>"); // XSS if userName contains <script>
// SAFE
Write("<div>Hello, " + htmlEncode(userName) + "</div>");
7. Restrict Sensitive Data in Responses
Don’t expose internal identifiers, full DE records, or stack traces in error responses:
// BAD — leaks internal structure
} catch(e) {
Write(Stringify(e));
}
// GOOD — safe error message
} catch(e) {
Platform.Function.InsertData("ErrorLog",
["timestamp", "message", "stack"],
[Platform.Function.Now(), e.message, e.stack || ""]
);
Write(Stringify({ status: 500, statusMessage: "Internal Server Error", error: "An internal error occurred" }));
}
8. Rate Limiting with DE
CloudPages don’t have built-in rate limiting. Implement it with a DE:
function formatDate(dateString,dateFormat,timeFormat,isoLocale) {
Platform.Variable.SetValue("@formatDate_string",dateString);
Platform.Variable.SetValue("@formatDate_date",dateFormat);
Platform.Variable.SetValue("@formatDate_time",timeFormat);
Platform.Variable.SetValue("@formatDate_iso",isoLocale);
return Platform.Function.TreatAsContent("%%=FormatDate(@formatDate_string, @formatDate_date, @formatDate_time, @formatDate_iso)=%%");
}
var ip = Platform.Request.GetRequestHeader("X-Forwarded-For")
|| Platform.Request.GetRequestHeader("REMOTE_ADDR")
|| "unknown";
var timeWindow = formatDate(Platform.Function.Now(), "MM/DD/YYYY HH:mm");
var key = ip + "|" + timeWindow;
var hitCount = Platform.Function.Lookup("RateLimit", "count", "key", key);
hitCount = parseInt(hitCount, 10) || 0;
if (hitCount >= 10) { // 10 requests per minute
Write(Stringify({ status: 429, statusMessage: "Too Many Requests", error: "Rate limit exceeded" }));
return;
}
Platform.Function.UpsertData("RateLimit",
["key"], [key],
["count", "window"],
[hitCount + 1, timeWindow]
);