Privacy masking and blocking sensitive data

Acoustic Analytics provides a tiered approach to mask sensitive information selectively. You can perform privacy masking in the user's browser to ensure highly sensitive information never reaches the Acoustic servers. Alternatively, you have options to selectively mask captured data on the Acoustic servers for maximum flexibility. For example, you can use hit attributes to block sensitive information. For more information, see Creating hit attributes.

The following types of privacy masking can be performed in the user's browser and require Web SDK to be configured accordingly:

Privacy masking of sensitive user input

This type of privacy masking applies to HTML input fields such as input, select, and textarea elements where the user enters sensitive information.

You can configure the privacy rules as a whitelist or a blacklist. In a whitelist configuration (the default), the rules specify which input fields to apply the privacy masking. In a blacklist configuration, the rules specify which input fields do not apply privacy masking. All input fields that are not specified in a blacklist are masked.

To create a rule, the following information is needed:

  • Whitelist or blacklist
    You must determine whether the rule should act as a whitelist (the default), where the target elements specified are masked. Or should the rule act as a blacklist, where the mask is applied to input elements that DO NOT match the target list.
  • Element identification
    Elements can be identified by their id and idType, which can be found either from the JSON message that is accessible during replay or by using a CSS selector. When using an id and idType, the id can be a regular expression that matches a set of input elements.

📘

Note:

Input elements in a frame/iframe should always be identified using a CSS selector.

  • Mask type
    You must select one of the three built-in masking functions or you can create your own custom masking function.

The following table illustrates how each of the supported masking types will evaluate the input value "HelloWorld123":

Mask TypeMasked ValueDescription
(1) EMPTY" "The replacement is an empty string.
(2) BASIC"XXXXX"he replacement is a fixed string.
(3) TYPE"XxxxxXxxxx999"Each character is replaced by its character class.
(4) CUSTOMCustomThe replacement value returned by the custom function.

Configuration examples

The input privacy configuration is specified in the services.message.privacy object of the JSON configuration.

message: {
    privacy: [
        {
            targets: [
                // CSS Selector: All password input fields
                "input [type=password]"
            ],
            maskType: 3
        }
    ],
    ...
}

For each maskType, there needs to be a separate entry as follows:

message: {
    privacy: [
        {
            targets: [
                // CSS Selector: All password input fields
                "input [type=password]"
            ],
            maskType: 3
        },
        {
            targets: [
                {
                    id: "ssn"
                    idType: -1
                }
            ],
            maskType: 1
        }
    ],
    ...
}

For a given maskType, the list of input elements that the privacy masking to be applied should be specified in the targets array.

targets: [
    {
        id: "ssn",
        idType: -1
    },
    {
        id: {
            regex: "^creditCard.*",
            flags: "g"
        },
        idType: -1
    }
]

Input elements identified by CSS Selectors are specified in the targets array as follows:

targets: [
    "input[type=password]",
    ".pii"
]

The targets array can contain a mix of input elements identified by id, idType, and CSS Selectors as follows:

targets: [
    {
        id: "ssn",
        idType: -1
    },
    {
        id: {
            regex: "^creditCard.*",
            flags: "g"
        },
        idType: -1
    },
    "input[type=password]",
    ".pii"
]

Privacy masking by using a custom function can be configured by setting the maskType to 4 and specifying the custom masking function in maskFunction as follows:

message: {
    privacy: [
        {
            targets: [
                {
                    id: "accountNumber",
                    idType: -1
                }
            ],
            maskType: 4,
            maskFunction: function (val) {
                var maskedVal = "XXXXXX";
                if (val.length === 10) {
                    // Only expose last 4 digits of account
                    maskedVal += val.substr(-4);
                }
                return maskedVal;
            }
        }
    ],
    ...
}

To configure a privacy blacklist, you should add the exclude flag as follow:

message: {
    privacy: [
        {
            exclude: true,
            targets: [
                // Mask all input excluding email.
                {
                    id: "email",
                    idType: -1
                }
            ],
            maskType: 2
        }
    ],
    ...
}

Best practices

  • Use static and unique HTML IDs to identify sensitive input fields and specify these in the privacy configuration for maximum performance.
  • Validate that CSS selectors and regular expressions are evaluating as expected and not causing any performance issues.
  • Do not rely on xpath IDs for privacy. If xpath IDs are unavoidable, ensure adequate testing is performed so that site content changes do not invalidate the privacy rule. Try to minimize the xpath length by adding a static and unique HTML ID to the nearest container element.

Troubleshooting - When using id and idType to identify elements, ensure they match the values reported in the raw JSON.

  • Validate the syntax. Often a missing comma or a typo in the property name or value (e.g. specifying idtype instead of idType) will cause a failure.
  • When using multiple privacy rules, selectively remove individual rules to help isolate the problem.

Privacy masking of custom attributes and inner text logged with type 4 and type 9 messages.

Logging of custom attributes is an optional feature. When this feature is used there may be a requirement to mask sensitive information in the attributes or inner text of certain target elements.

Privacy masking of custom attributes uses the same configuration as that described in the Privacy masking of sensitive user input. The attributes can be masked completely by setting the maskAttributes property to true in the privacy configuration. Granular control can be achieved by specifying a function which accepts the element id and attribute list. This function can be used to selectively apply the privacy masking on specific attribute(s).

Configuration examples

message: {
    privacy: [{
            targets: [
                // CSS Selector: All password input fields
                "input [type=password]"
            ],
            // All attributes will be masked using maskType 3.
            maskAttributes: true,
            maskType: 3
        }, {
            targets: [{
                    id: "ssn"
                    idType: -1
                }
            ],
            maskType: 1,
            /*
             * Custom function used to perform selective attribute masking.
             * @param {String} id Element id
             * @param {Object} attributes Object containing the name-value pairs
             * @returns {Object} Object containing the masked values which will be logged by the UIC.
             */
            maskAttributes: function (id, attributes) {
                if (attributes.secret) {
                    // apply selective privacy to the attribute containing sensitive information.
                    attributes.secret = "BLOCKED";
                }
                // Return the object which will be logged by the UIC.
                return attributes;
            }
        }
    ],
    ...
}

DOM Privacy - Privacy masking of sensitive information contained or displayed by the application itself

📘

Note:

DOM Privacy masking is for release 5.4 and up

This type of privacy masking applies to the HTML content, which displays sensitive information in the application or specifies sensitive information within the element attributes.

Similar to Privacy masking of sensitive user input, you can configure the privacy rules by specifying an element target and a custom masking function. When a DOM snapshot is taken, any element that matches the rule will be passed to the custom masking function, which can manipulate the DOM element as required.

Alternatively, the privacy rules can also be configured as a regular expression pattern that matches portions of the HTML content. A replacement string can be specified that will be used to replace all matches within the HTML.

To create a rule, the following information is needed:

  • Match pattern
    The pattern to be matched within the HTML content is specified as a regular expression.

  • Replacement string

Configuration examples
DOM privacy masking of elements using their element id or CSS selector.

privacy: [{
        targets: [{
                id: "accNum",
                idType: -1
            },
            ".tlPrivate"
        ],
        maskType: 4,
        maskFunction: function (val, element) {
            if (element && element.innerText) {
                // Directly modify the DOM element
                element.innerText = "BLOCKED BY TEALEAF (UIC SDK)";
            }
            // Return unmasked value. May choose to apply masking to the value as appropriate.
            return val;
        }
    }
],

For DOM privacy masking using RegEx patterns, the configuration is specified in the services.message.privacyPatterns object.

message: {
    privacyPatterns: [
        {
            pattern: {
                regex: "\\d{3}-\\d{2}-\\d{4}",
                flags: "g"
            },
            replacement: "XXX-XX-XXXX"
        }
    ],
    ...
}

For each unique pattern to be matched and replaced, there needs to be a separate entry in the list as follows:

privacyPatterns: [
    {
        // Match Social Security numbers
        pattern: {
            regex: "\\d{3}-\\d{2}-\\d{4}",
            flags: "g"
        },
        replacement: "XXX-XX-XXXX"
    },
    {
        // Match phone numbers
        pattern: {
            regex: "\\d{3}-\\d{3}-\\d{4}",
            flags: "g"
        },
        replacement: "XXX-XXX-XXXX"
    },
    {
        // Match US zipcode
        pattern: {
            regex: "\\d{5}-\\d{4}",
            flags: "g"
        },
        replacement: "XXXXX-XXXX"
    }
]

A custom function can be specified instead of a fixed replacement string. In this situation, the function will receive the matched string as an argument and is expected to return the replacement string.

privacyPatterns: [
    {
        // Match Social Security numbers and mask the first 5 digits.
        pattern: {
            regex: "\\d{3}-\\d{2}-\\d{4}",
            flags: "g"
        },
        replacement: function (ssn) {
            var retVal = "XXX-XX-";
            retVal += ssn.substr(-4);
            return retVal;
        }
    }
]

Another example of a custom function used with RegEx capturing groups to achieve a complex privacy masking result:

privacyPatterns: [
    /**
     * Example of a customized masking function to select and partially mask multiple groups.
     * HTML to be masked: <div>Jane Smith's Account Number: 98776543</div>
     * Masked result: <div>Jane XXXXX Account Number: XXXXX543</div>
     * Note the double \\ escaping in the regex string to account for JSON string parsing.
     */
    {
        pattern: {
            regex: "<div>(.*)\\sAccount Number:\\s*(\\d+)<\\/div>",
            flags: "g"
        },
        replacement: function (fullMatch, group1, group2) {
            var replacementNumber = "XXXXX" + (group2.length >= 8 ? group2.substr(-3) : ""),
                nameSplit = group1.split(" "),
                replacementName = nameSplit.shift() + " XXXXX",
                retVal;

            retVal = fullMatch.replace(group1, replacementName).replace(group2, replacementNumber);

            return retVal;
        }
    }
]

Best practices

  • Perform adequate testing to check if the replacement patterns are working as intended and not causing any performance issues.
  • Use this feature for specifically masking HTML content that cannot be sent to the Acoustic Cloud servers. Do not use it as a general-purpose masking tool. Use the option to mask data on the Acoustic server instead.

Preventing the capture of any user interaction within a subsection of the page

This type of privacy masking applies when the application wants to block the recording of any user interaction events (click, tap, etc.) with specific content. This type of blocking is needed in extremely rare circumstances where the user interaction itself may give away sensitive information.

📘

Note:

This blocking does not remove or mask the HTML content itself. It only blocks the recording of user interaction events with the content.

For example, if the application displays a virtual keyboard to enter a password, recording the "click" action on the virtual keyboard could expose the user's password.

The blocking rule can be configured as a CSS selector that matches an element. Any user interaction with the element or its descendants will not be recorded.

To create a rule, the following information is needed:

  • CSS Selector of the element

Configuration examples
The configuration for blocking the recording of any user interaction with specific content is specified in the core.blockedElements object.

Consider that an application displays a virtual keyboard for the user to click on to enter their password. If the virtual keyboard is contained within a container div element, then all user interactions with the virtual keyboard can be prevented from being recorded by specifying a CSS selector for the div element.

<div id="virtualKeyboard" class="keyboard">
   ...
</div>

The configuration to block the capture of any user interaction with this can be specified as:

core: {
    // List of CSS selectors corresponding to elements for which no user interaction is to be reported.
    // WARNING: Since this list is evaluated for each event, specifying inefficient selectors can cause performance issues.
    blockedElements: [
        "#virtualKeyboard"
    ],
    ...
}

To configure your privacy pattern to work across multiple lines, you can use a character class (\s) and its negation (\S) together as follows:

[\s\S]
Consider a panel where sensitive info is being displayed that can span multiple lines.

Please confirm the description of your health issue:
<div id="health_description">
...
</div>
<input type="button" id="health_description_confirmation" value="Confirm" />

Since the "." doesn't match the newline character in JS RegEx, use the alternative [\s\S] until such time as all browsers support the dotAll flag:

message: {
    privacyPatterns: [
        {
            pattern: { regex: "<div[^>]*?id=\"health_description\"[\\s\\S]*?<\\/div>", flags: "g"},
            replacement: ""
        }
    ]
}

Best practices

  • Perform adequate testing to check if the blocking is working as intended and not causing any performance issues.
  • Use this feature for only blocking the recording of user interactions. This feature cannot be used to block the content itself from being captured as part of a DOM snapshot. To block the HTML content refer to Privacy masking of sensitive information contained or displayed by the application itself. Note: DOM Privacy masking is for release 5.4 and up.

Privacy masking of sensitive information by creating a class in the application

You can create a class in the application and use the class to block items.

privacy: [{
targets: [
".tlPrivate"
],
"maskType": 3 // whichever mask type suites your needs
}

📘

Note:

This method blocks anything related to the class; therefore, it is important to use a unique class name.