Enable privacy protection in the Connect Web SDK
Before releasing your web app with the Connect library to production, you must configure the library to restrict capturing your end users' personally identifiable information (PII).
For practical purposes, we distinguish between two categories of data to protect.
- User input data. It originates from interaction with user interface elements in your web application. Typically, these are text input fields, but these can also be drop-down lists, date/time selectors, radio buttons and checkboxes.
- Page content displayed by your web application. For example, a user can visit the Your account section and view their home address, phone number and email address. To prevent the library from capturing this data, configure the SDK to remove it from the DOM snapshots. Conditional logic is supported.
Protecting user input data
This section explains how to enable privacy protection for interactive elements. The workflow varies slightly depending on where the library is hosted: in the Acoustic CDN or on your own server.
If your integration snippet is linking to our CDN, you will need to add your privacy settings to it. See examples below.
If you are hosting the library on your own server, here are steps to follow.
- Open acoconnect.js.
- Find the
services.message.privacy
object. - Configure your privacy rules (see examples below).
- (recommended) Validate the syntax in the file.
- Push the changes to the server.
White-list or black-list approach
- If your website has many interactive elements collecting visitors' personal data, consider the white-list approach. If you set the
exclude
parameter totrue
, the library will exclude all user input from capturing except for the HTML elements you specify as targets.
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacy.push({
exclude: true,
targets: ["input[type=search]", {
id: "country",
idType: -1
}],
maskType: 1
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
message: {
privacy: [{
exclude: true,
targets: [
"input[type=search]",
{
id: "country",
idType: -1
}
],
maskType: 1
}]
}
- If you'd rather specify which interactive elements to exclude from capturing, go with the black-list approach. To enable it, set
exclude
tofalse
.
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacy.push({
exclude: false,
targets: [
"input[type=email]",
"input[type=tel]",
"select[form=application]",
"textarea[form=application]"
],
maskType: 1
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
message: {
privacy: [{
exclude: false,
targets: [
"input[type=email]",
"input[type=tel]",
"select[form=application]",
"textarea[form=application]"
],
maskType: 1
}]
}
Creating a rule for a group of HTML elements
You can create a rule for a group of interactive elements. To identify elements within the group, use CSS attribute selectors and class selectors.
Important
Use only the attributes of the input element, not of its parent elements. For example, the class of the
<div>
element isn't a valid identifier for a privacy rule.
This is how you can allow the library to capture input fields that do not contain any PII:
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacy.push({
exclude: true,
targets: [
"input[name*=model]",
"select[id=group_1]",
".district"
],
maskType: 3
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
message: {
privacy: [{
exclude: true,
targets: [
"input[name*=model]",
"select[id=group_1]",
".district"
],
maskType: 3
}]
}
Creating a rule for a particular HTML element
You can create a rule based on the IDs of interactive elements. The ID of each element must be unique within a page.
Warning
This approach doesn't work for input elements within a frame/iframe.
You can specify the ID either as a CSS selector (#myid
) or as an identifier of type -1
. Both are valid. Here is an example.
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacy.push({
exclude: false,
targets: [
"#ssn", // ID specified as CSS selector
{
id: "symptoms", // ID specified by name and idType
idType: -1
}
],
maskType: 2
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
message: {
privacy: [{
targets: "#ssn",
{
id: "symptoms",
idType: -1
}],
maskType: 2
}]
}
Advice
You can combine different types of targets within a privacy rule as long as they require the same mask type.
Summary of required configuration parameters
Parameter | Values | Description |
---|---|---|
exclude | Boolean. Default value - false . | Determines how a rule works. - If set to false , the targets are treated as a blacklist. The library captures user input from all UI elements except for those specified in the target.- If set to true , the targets are treated as a whitelist. The library captures user input only from UI elements in the target. Interactions with other elements are not captured. |
targets | Array | Identifies the interactive elements that the rule applies to. Use CSS selectors, nested objects or a mixture of both. |
maskType | 1 - the field appears blank2 - the replacement is a fixed string ("XXXXX")3 - each character is replaced by its character class (x, X, 9 or @).4 - custom (your own masking function) | Defines how protected data is displayed in session replays. |
The targets
parameter can contain objects. Each object requires two parameters: id
and idType
.
Parameter | Values | Description |
---|---|---|
id | String | The identifier of an HTML element. It must be unique. |
idType | Integer. Supported values:-1 - stands for the HTML id attribute-2 - stands for an XPath | Indicates what kind of identifier is being used. |
Best practices
- Do not rely on XPath IDs for privacy. If they are unavoidable, ensure adequate testing is performed so that content changes do not invalidate your privacy rules. Try to minimize the XPath length by adding unique static HTML ID to the nearest container element.
- If you run into an issue while using multiple privacy rules, try selectively removing individual rules to isolate the issue.
Masking page content
There are two approaches to masking page content displayed by your web application or contained within the attributes of a UI element:
- Using a custom masking function (recommended)
- Using DOM privacy patterns
Option A: Adding a custom masking function for page content
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 to mask content before the snapshot is sent to the Acoustic Cloud servers.
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacy.push({
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 SDK";
}
// Returns unmasked value. May choose to apply masking to the value as appropriate.
return val;
}
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
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 SDK";
}
// Returns unmasked value. May choose to apply masking to the value as appropriate.
return val;
}
}],
For the list of supported parameters, see Enable privacy protection in the Connect Web SDK.
Option B: Adding DOM privacy patterns
DOM privacy patterns are regular expressions that match against the HTML content in the DOM snapshots taken by the Connect library so specific content can be masked before the snapshot is sent to the Acoustic Cloud servers. They are a useful alternative to custom masking functions in some circumstances, but are less efficient.
To enable a privacy pattern for page content data, open acoconnect.js and find the services.message.privacyPatterns
object. The DOM snapshots containing the page HTML are treated as blocks of text against which the regular expressions are applied, with the aim of replacing PII with a mask such as "XXXXX".
Parameter | Nested objects | Values | Description |
---|---|---|---|
pattern | regex | A regular expression matching HTML content to be masked | |
pattern | flags | String | Option flag specifying regular expression behaviour (e.g. case sensitive or not, match all occurrences or not, etc.) |
replacement | N/A | String or function | Text to display in place of the restricted values |
Advice
Make sure the replacement patterns work as intended and don't cause any performance issues.
Example with string replacements
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacyPatterns.push({
// Restrict Social Security numbers
pattern: {
regex: "\\d{3}-\\d{2}-\\d{4}",
flags: "g"
},
replacement: "XXX-XX-XXXX"
}, {
// Restrict phone numbers
pattern: {
regex: "\\d{3}-\\d{3}-\\d{4}",
flags: "g"
},
replacement: "XXX-XXX-XXXX"
}, {
// Restrict US zipcode
pattern: {
regex: "\\d{5}-\\d{4}",
flags: "g"
},
replacement: "XXXXX-XXXX"
}
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
message: {
privacyPatterns: [{
// Restrict Social Security numbers
pattern: {
regex: "\\d{3}-\\d{2}-\\d{4}",
flags: "g"
},
replacement: "XXX-XX-XXXX"
},
{
// Restrict phone numbers
pattern: {
regex: "\\d{3}-\\d{3}-\\d{4}",
flags: "g"
},
replacement: "XXX-XXX-XXXX"
},
{
// Restrict US zipcode
pattern: {
regex: "\\d{5}-\\d{4}",
flags: "g"
},
replacement: "XXXXX-XXXX"
}
]
}
Example of a full function replacement
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacyPatterns.push({
pattern: {
regex: "\\d{3}-\\d{2}-\\d{4}",
flags: "g"
},
replacement: function(ssn) {
var retVal = "XXX-XX-";
retVal += ssn.substr(-4);
return retVal;
}
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
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;
}
}
]
Example of a partial function replacement
<script src="https://cdn.goacoustic.com/connect/latest/acoconnect.js"></script>
<script>
let config = TLT.getDefaultConfig();
// This will append additional privacy rules.
config.services.message.privacyPatterns.push({
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;
}
})
window.TLT && window.TLT.initLibAdv("YOUR APP KEY", "YOUR ENDPOINT URL", config, true, true, true, true, true);
</script>
privacyPatterns: [
/**
* 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;
}
}
]
Updated about 1 month ago