Securing CloudPages with SSJS in Salesforce Marketing Cloud

If you ever wanted to restrict access to a landing page or an endpoint created using a CloudPage, I’ve got two options for you, that only require a short code snippet and work as a simple way to govern who is able to access your page.

IP Address Whitelisting

Restricting access based on IP addresses is pretty easy and if your landingpage is supposed to be accessible only within a company/client’s network, most of the time restricting to a few IP addresses is sufficient as many companies have just a small number of public IP addresses that are shared amongst employees at a specific office location.

Furthermore, this solution also works for processing endpoints that are called from specific servers only. In that case, this can be combined with the next approach that uses API keys and secrets to restrict access.

The code below is an example for IP address filtering, where all the allowed IP addresses are contained in the “allowedIPs”-array.

<script runat="server">
    Platform.Load("Core", "1");

    var allowedIPs = [
        "xxx.xxx.xxx.xxx"
    ];

    try {
        var validIp = false;
        for (var i = 0; i < allowedIPs.length; i++) {
            if (allowedIPs[i] == Platform.Request.ClientIP) {
                validIp = true;
            }
        }

        if (validIp) {
</script>

INSERT YOUR REGULAR CLOUDPAGE CODE HERE

<script runat="server">
        } else {
            // Request originates from wrong IP
            throw new Error("Non-whitelisted IP");
        }
    } catch (e) {
    	// You could also redirect to an error page here.
    	// This catch-block catches any error within the try-block, so not only wrong IP addresses
        Write(Stringify(e));
    }
</script>

API Secret/Key verification for processing endpoints

This solution isn’t applicable for regular landingpages you visit using your webbrowser, as it is designed for processing endpoints created using CoudPages. The code verifies if the host sending a request to your CloudPage passes the correct credentials that are key/value pairs within the “allowedCredentials”-object in the example below. If there is a mismatch an error is returned, otherwise the processing continues.

In order to have an even more solid solution in place, you can also combine this method with whitelisting IP addresses like above.

<script runat="server">
    Platform.Load("Core", "1");

    var allowedCredentials = {
        "key": "secret"
    };

    try {
        var jsonpost = Platform.Request.GetPostData();
        if (jsonpost) var json = Platform.Function.ParseJSON(jsonpost);

        /*
         * Check if all the required variables and properties
         * are set and if the credentials are correct
         **/
        if (json && json.auth && json.auth.apiKey && json.auth.apiSecret
                && allowedCredentials[json.auth.apiKey] == json.auth.apiSecret) {

            // Remove auth data from json
            delete json.auth;

            /* INSERT YOUR ENDPOINT LOGIC HERE*/

        } else {
            // Authentication failed
            throw new Error("Wrong credentials");
        }
    } catch (e) {
        var error = {
            "success": false,
            "message": Stringify(e)
        };
        Write(Stringify(error));
    }
</script>

The above is just a minimal example for a POST-Endpoint. To further enhance it, you could store encrypted versions of the API keys and secrets within the source code and decrypt them on the fly using encryption keys stored in Marketing Cloud’s “Key Management” feature. Unfortunately this isn’t possible with pure Server-Side JavaScript, so you’d have to mix SSJS with AMPscript code to achieve this.

An alternative enhancement could be storing the API keys and secrets within a data extension and loading them in your CloudPage. However, this is more of a scaleability improvement as adding further credentials is easier and doesn’t require code changes.

If you don’t want to send the api secret and key in the payload, this snippet can easily be modified to transmit them in the request headers for example.

Note: It isn’t best practice to use CloudPages to do heavy lifting and provide an extensive API, so please make sure you do lightweight processing and only stuff that makes sense within Salesforce Marketing Cloud.

General tip

Always make sure you have a SSL certificate configured for CloudPages in your account, so the transmission between the client and the CloudPage is encrypted. If this isn’t set up yet, contact your account executive in order to enable it. Note: If not already included in your contract, this feature comes at an additional charge.

Further reading

Disclaimer / Comments

This isn’t meant as a high security measure, so don’t expose highly sensitive data or critical function via this or any other CloudPage!

Salesforce’s recommendation for entire data kept in Marketing Cloud is as follows:

NEVER use Marketing Cloud to transmit or store the following types of data:

Government-issued identification numbers, including (but not limited to):

  • Social Security numbers
  • Passport numbers
  • Drivers license numbers

Any financial account numbers, including (but not limited to):

  • Credit or debit card numbers
  • Bank account numbers
  • Any other similar identifiers as defined by the Payment Card Data Security Standards

If you are familiar with JavaScript, but not with Server-Side JavaScript in Salesforce Marketing Cloud, parts of the code examples might seem a bit inefficient/complicated, but this is due to the fact that SSJS only supports ECMAScript 3 and even some polyfills don’t work.