Personal data handling
Personal Data handling in the HTML (in Session Replay)
Section titled Personal Data handling in the HTML (in Session Replay)Sessions will be collected for Session Replay if:
- The
Session Replay
feature has been enabled for your account - The threshold set by the
Session Replay Collection Rate
is not exceeded.
For the HTML content to be sent, all these conditions must be true:
- The user hasn’t opted out.
- The session is being tracked.
- The browser is compatible.
- The session has been flagged as “Collected for Session Replay”, using the
_cs_s
cookie - See the full list
Session replay data collection options
Section titled Session replay data collection optionsIf Session Replay is turned on and data collection is active for the session, the HTML content of the page is sent after every pageview (both natural and artificial) and every DOM change (only the updated elements are sent).
With automasking off
Section titled With automasking off- The content of
<input>
elements with typetext
,email
,password
,search
,tel
,url
or<input>
elements without atype
attribute are replaced with bullets before the HTML is sent, - Every figure of
<input>
elements with typenumber
are replaced by 0, - The content
<textarea>
elements is replaced with bullets before the HTML is sent, - The content of
<script>
elements is emptied, - Except for the above, all the HTML content is sent, including
<head>
, - The content of every element marked with the data attribute
data-cs-mask
is removed (see below).
With automasking on
Section titled With automasking on- The content of
<input>
elements with typetext
,email
,password
,search
,tel
,url
or<input>
elements without a type are replaced with bullets before the HTML is sent, - Every figure of
<input>
elements with typenumber
are replaced by 0, - The content
<textarea>
elements is replaced with bullets before the HTML is sent, - The content of
<script>
elements is emptied, - The content of every element marked with the data attribute
data-cs-mask
is removed (see below). - The content of every element marked with the data attribute
data-cs-capture
is collected and shown unless it falls under the excluded elements listed above (<input>
,<textarea>
) - Every character of the text content included in the elements sent is replaced by “A”.
- Attributes unrelated to layout are removed — we only keep
id
,class
,style
,src
,srcset
,href
,rel
andtype
.
Selectively mask pages by URL
Section titled Selectively mask pages by URLIn case your site displays Personal Data only on a specific set of pages, you can decide to run the Auto-Masking process only on a pre-defined set of pages, so that you won’t have to apply the masking across the whole site.
We currently have two available options:
- Mask all pages except: All pages will be masked except the ones defined by a Regex Pattern agreed with your dedicated Implementation Manager.
- Unmask all pages except: All pages will be collected with clear content except from the ones defined by a Regex pattern agreed with your dedicated Implementation Manager.
These can only be configured by your dedicated Implementation Manager.
Block data collection for specific pages (Session Replay)
Section titled Block data collection for specific pages (Session Replay)Use the excludeURLforReplay
command to block data collection on pages based on their URL.
You might want to use this command for the following reasons:
- Some page replays for Session Replay cause performance issues (when the page contains many images or videos, for instance) — you can block these pages until the problem is fixed,
- Personal Identifiable Information (PII) has been exposed,
- You want to exclude whole parts of your website from being collected for Session Replay.
window._uxa = window._uxa || [];window._uxa.push(['excludeURLforReplay', <URL_REGEX>]);
Using this command does not block page views for the given page(s) — it only blocks data collection for Session Replay.
Remove content/Personal Data from the collected HTML
Section titled Remove content/Personal Data from the collected HTMLThere are two methods to remove content from the collected HTML:
- Using the Contentsquare Personal Data Selector
- Tagging DOM Elements
Personal Data Selector
Section titled Personal Data SelectorEach DOM element to be masked is identified thanks to its CSS selector and those selectors are pushed to the API via a JavaScript object. The content of the identified element will be thus removed from the replay.
The following object that contains CSS selectors for text and for attributes should be pushed to the API as follows, before firing the main tag:
window._uxa = window._uxa || [];window._uxa.push(['setPIISelectors', {PII Object}]);
The structure of the PII
Object is as follows:
-
PIISelectors
: Comma-separated list of CSS selectors which allow the matching DOM elements to be masked by the tag. -
Attributes
: Using this key, one can remove the defined attributes of the DOM elements from the elements matching the CSS selectors. Note that it is possible to use an array of attribute names as shown in the example above.
Keep in mind that using both PII Selectors
and Attributes
is optional, meaning you can use either full element selectors, specific attribute selectors or both in the same object:
{ PIISelectors: [".css-selector, selector"], // DOM elements to be masked Attributes: [ { selector: "select#month option, select#year option", // CSS selector(s) attrName: 'id' // Attribute name you want to mask }, { selector: ".link-page-7", // CSS selector(s) attrName: ['href','name'] // Array attribute names you want to mask } ]};
Tagging DOM elements
Section titled Tagging DOM elementsTo exclude HTML from the page where data collection is active, add the data-cs-mask
attribute to the HTML element(s): their content will be removed in the browser, before the content is even sent to Contentsquare.
HTML of the page |
|
---|---|
Resulting HTML Collected |
|
You can also add the data-cs-mask
attribute with JavaScript provided you do it before any pageview is sent.
Events on removed HTML — Events (click, hover, etc.) will still be collected and sent to our servers even if they are targeting removed HTML elements. They will be visible in the application using Live Resources (as named in the platform).
Display content in a fully masked HTML page
Section titled Display content in a fully masked HTML pageThere are two methods to display content in a masked HTML page:
- Using the Contentsquare Personal Data Selector
- Tagging DOM Elements
Personal Data Selector
Section titled Personal Data SelectorEach DOM element to be displayed is identified thanks to its CSS selector and those selectors are pushed to the API via a JavaScript object. The content of the identified element will be thus displayed in Session Replay.
The string which contains comma-separated CSS selectors for text and for attributes must be pushed to the Contentsquare API, before firing the main tag, as follows:
window._uxa = window._uxa || [];window._uxa.push(['setCapturedElementsSelector', "#capture-me, .show-me"]);
Tagging DOM elements
Section titled Tagging DOM elementsTo display an HTML element in a masked page, add the data-cs-capture
attribute to the HTML element.
HTML of the page |
|
---|---|
Resulting HTML Collected |
|
It can also be added with JavaScript but, be careful, as attributes must be added before any pageview is sent.
Session Replay Testing
Section titled Session Replay TestingPrerequisites
Section titled Prerequisites- Have Google Chrome installed.
- Download the Contentsquare Tracking Setup Assistant Chrome Extension.
- Add the
data-cs-mask
attribute to exclude elements from the tracking. Data within form fields is not captured by default, except dropdowns which still need masking. - Activate full session masking; no regular expressions should be configured.
- If you have already provided us with a list of your internal/office IPs for us to add to our exclusion list and you are planning to run Session Replay tests, inform your Contentsquare Implementation Manager or Success Manager so we can temporarily pause that exclusion list. If you don’t, and run test Session Replays from an IP that is in our exclusion list, your test session will not be collected in Contentsquare.
Testing
Section titled Testing-
Start a journey on your website, for instance a checkout funnel.
-
In the Contentsquare Tracking Setup Assistant extension, select
Turn off anonymisation
to prevent ‘AAA’ masking from effecting your local session. -
Select
Recorded for Session Replay
to enable Session Replay data collection for your session. -
Select
Trigger testing dvar
to create theSR Testing
dynamic variable which identifies your current session.Data collection for Session Replay is now active for your session and you can proceed with the rest of your journey.
-
Fill out any test personal information which are required along the way.
Such information is usually displayed as a form of confirmation — this is where you will want to check it is masked. -
Once you are done testing, close your session by either closing the session’s tab or deleting your cookies.
-
After 30 minutes, log in to the Contentsquare platform and find the Session Replay marked with the
SR Testing
dynamic variable. -
Watch the replay and look for all the areas where Personal Data could appear:
- ❌ If Personal Data is visible, update your masking and test again.
- ✅ If there were no Personal Data, you are covered in terms of GDPR compliance.
Following Session Replay requests
Section titled Following Session Replay requestsHTML content is sent via requests to one of the endpoints listed below, based on where data is stored for your project:
https://k.aeu1.contentsquare.net/{apiVersion}/recording
https://k.aus1.contentsquare.net/{apiVersion}/recording
https://k.eu1.az.contentsquare.net/{apiVersion}/recording
https://k.us1.az.contentsquare.net/{apiVersion}/recording
https://k.ba.contentsquare.net/{apiVersion}/recording
https://k.bf.contentsquare.net/{apiVersion}/recording
https://k.aa.contentsquare.net/{apiVersion}/recording
https://k.af.contentsquare.net/{apiVersion}/recording
The request is sent with the _cs_s
cookie, which value ends .3
:
The request contains all the DOM.
Blocking Personal Data in URLs
Section titled Blocking Personal Data in URLsPersonal Data does not only appear within HTML content.
For a given page, you also need to mask Personal Data found in:
- The path of the current page
- The query string parameters of the current page
- The referrer of the next visited page
Unmasked data could be exposed in the Error Analysis module.
Masking Personal Data in the URL path
Section titled Masking Personal Data in the URL pathTo remove Personal Data in the URL of the current page, we must rewrite the URL of the pageview, and mask it, before the tag collects it, using the setPath
command.
The setPath
command takes as a parameter the exact new path we want to collect as a URL.
window._uxa = window._uxa || [];window._uxa.push([ 'setPath', <NEW_PATH_TO_COLLECT>]);
This command needs to be fired on every page where the URL contains Personal Data, before the main Tracking tag is loaded. Only the path is to be configured, not the whole URL.
Example
Section titled Examplewindow._uxa = window._uxa || [];window._uxa.push([ 'setPath', '/users/CS_ANONYMIZED_USER_ID']);
URL before anonymization | URL after anonymization |
---|---|
https://www.contentsquare.com/users/jon.snow | https://www.contentsquare.com/users/CS_ANONYMIZED_USER_ID |
https://www.contentsquare.com/cartpage/users/jon.snow | https://www.contentsquare.com/users/CS_ANONYMIZED_USER_ID |
Masking Personal Data in URL query string parameters
Section titled Masking Personal Data in URL query string parametersWhen Personal Data can be found in the query parameters of an URL, use the setQuery
command to replace the query string within the URL with a new masked one.
window._uxa = window._uxa || [];window._uxa.push([ 'setQuery', <ANONIMYZED_QUERY_STRING>]);
The setQuery
command takes as a parameter the exact new query string we want to display within the URL. This command instructs the Tag to replace the part of the URL after the ?
by another one with masked values.
This command must be fired on all pages where the URL contains Personal Data, and before the main tag is loaded. Only the query string is to be configured, not the whole URL.
Example
Section titled Examplewindow._uxa = window._uxa || [];window._uxa.push([ 'setQuery', '?user_id=CS_ANONYMIZED_USER_NAME&address=CS_ANONYMIZED_ADDRESS']);
URL before anonymization | URL after anonymization |
---|---|
https://www.mysite.com/us/makeup/valentines-day/?user_id=jon.snow&address=castle.black | https://www.mysite.com/us/makeup/valentines-day/?user_id=CS_ANONYMIZED_USER_NAME&address=CS_ANONYMIZED_ADDRESS |
https://www.mysite.com/us/makeup/valentines-day/?campaign=valentinesday_feb_2021&user_id=jon.snow&address=castle.black | https://www.mysite.com/us/makeup/valentines-day/?user_id=CS_ANONYMIZED_USER_NAME&address=CS_ANONYMIZED_ADDRESS |
Customize the masked term according to the “type” of the parameter we want to replace, in order for the result to be explicit:
CS_ANONYMIZED_USER_ID
CS_ANONYMIZED_EMAIL
CS_ANONYMIZED_ADDRESS
…and so on.
Masking Personal Data in the referrer
Section titled Masking Personal Data in the referrerEven when you have masked Personal Data in URLs and query parameters on a page, they propagate to the next visited page through the referrer
. To handle this use case, use the referrer:
commands below.
In the URL
Section titled In the URLConsider this journey:
- Users navigate from
https://www.example.com/users/123456/products/ABCDEF
tohttps://www.example.com/
. - A pageview is sent on
https://www.example.com/
. - The referrer value in both the browser and in the pageview request is
https://www.example.com/users/123456/products/ABCDEF
.
To mask Personal data in the URL within the referrer, use the referrer:maskUrl
command:
var referrers = [ 'https://www.example.com/users/:USER_ID/products/:PRODUCT_ID', 'https://www.example.com/account/cancelOrder/:ORDER_ID', 'https://www.example.com/order/:ORDER_ID/merge/:ORDER_ID'];window._uxa = window._uxa || [];for (var i = 0; i < referrers.length; i++) { window._uxa.push([ 'referrer:maskUrl', referrers[i] ]);}
The referrer value in the pageview request for https://www.example.com/
will be:
https://www.example.com/users/CS_ANONYMIZED_USER_ID/products/CS_ANONYMIZED_PRODUCT_ID
https://www.example.com/account/cancelOrder/CS_ANONYMIZED_ORDER_ID
https://www.example.com/order/CS_ANONYMIZED_ORDER_ID/merge/CS_ANONYMIZED_ORDER_ID
Within query parameters
Section titled Within query parametersConsider this journey:
- Users navigate from
https://www.example.com/confirmation?firstname=jon&lastname=snow
tohttps://www.example.com/
. - A pageview is sent on
https://www.example.com/
. - The referrer value in both the browser and in the pageview request is
https://www.example.com/confirmation?firstname=jon&lastname=snow
.
To mask Personal data in the query string part of the referrer, use the referrer:removeQueryString
command:
window._uxa = window._uxa || [];window._uxa.push([ 'referrer:removeQueryString']);
The referrer value in the pageview request for https://www.example.com/
will be:
https://www.example.com/?
Removing Personal Data in the API errors
Section titled Removing Personal Data in the API errorsRewrite the URL before the Tag is tracking the API errors and collecting the request URL, using the api-errors:maskUrl
command.
The api-errors:maskUrl
command takes the whole URL to display in replacement of the URL containing Personal Data as a parameter - not only the path or the query string.
window._uxa = window._uxa || [];window._uxa.push([ 'api-errors:maskUrl', <ANONYMIZED_URL_FOR_API_ERRORS>]);
This command must be fired on all pages containing Personal Data which call the Contentsquare API, and before the main tag is loaded.
Simply replace a step of the path - meaning between two slashes (/
) - containing Personal Data with a variable:
:user_id
becomesCS_ANONYMIZED_USER_ID
:address
becomesCS_ANONYMIZED_ADDRESS
- Following this pattern
:email
becomesCS_ANONYMIZED_EMAIL
Example
Section titled Examplevar urls = [ 'https://api.net/order/:order_id/merge/:order_id', 'https://api.net/order/:order_id/item', 'https://www.api.nl/nl/ajax/nfs/account/cancelOrder/:order_id'];window._uxa = window._uxa || [];for (var i = 0; i < urls.length; i++) { window._uxa.push([ 'api-errors:maskUrl', urls[i] ]);}
URL before anonymization | URL after anonymization |
---|---|
https://api.net/order/:order_id/merge/:order_id | https://api.net/order/CS_ANONYMIZED_ORDER_ID/merge/CS_ANONYMIZED_ORDER_ID |
https://api.net/order/:order_id/item | https://api.net/order/CS_ANONYMIZED_ORDER_ID/item |
https://www.api.nl/nl/ajax/nfs/account/cancelOrder/:order_id | https://www.api.nl/nl/ajax/nfs/account/cancelOrder/CS_ANONYMIZED_ORDER_ID |
Controlled Exposure
Section titled Controlled ExposureThe Contentsquare Tracking Tag does not collect any text typed into input
or textarea
fields.
If you need to display the personal data of a user, such as shipping/delivery addresses, names, phone numbers, email addresses, text inputs in a form field…, use our collect and encrypt mode.
The collect and encrypt mode allows for collecting, storing, and displaying text in input
, textarea
fields, or any other element within a page that is usually masked by the Tag, before it is sent to our servers.
Collect and encrypt personal data from the collected HTML
Section titled Collect and encrypt personal data from the collected HTMLThere are two methods to collect and encrypt personal data from the collected HTML:
- Using the Contentsquare Encryption Selector
- Tagging DOM Elements
Encryption Selector
Section titled Encryption SelectorEach DOM element to be collected and encrypted is identified via its CSS selector. These selectors are pushed to the API via a JavaScript object. The content of the identified element is then encrypted and collected for session replays.
The following object that contains CSS selectors for text and for attributes should be pushed to the API as follows, before firing the main tag:
window._uxa = window._uxa || [];window._uxa.push(['setEncryptionSelectors', {PII Object}]);
With several selectors:
window._uxa = window._uxa || [];window._uxa.push(['setEncryptionSelectors', "#cart, .total-items"]);
Tagging DOM elements
Section titled Tagging DOM elementsThe data-cs-encrypt
attribute can be added on an input field, textarea
field, or any other element.
Instead of being masked, the text present there will be encrypted, stored, and made available for exposing under specific conditions.
The encryption process happens on the innerText
property of the tagged element. It does not apply on nested elements: the text of the nested element won’t be masked nor encrypted. Make sure to add the data-cs-encrypt
attribute on the most granular elements that must be collected.
<h2 class="welcoming"> Welcome back <span class="user_name" data-cs-encrypt>John Doe</span></h2>
You can also add the data-cs-encrypt
attribute with JavaScript provided you do it before any pageview is sent.
Data Encryption
Section titled Data EncryptionTo use Controlled Exposure, all data to be collected must be encrypted. To do this, you will need to obtain a combination of encryption keys:
- Public key: is used by the Tracking Tag so we can encrypt the data
- Private key: is used by the APP for decryption
How to generate the key pair
Section titled How to generate the key pairOption 1: With a Google chrome script
Section titled Option 1: With a Google chrome script-
Copy the following script
function arrayBufferToString(buffer) {const byteArray = new Uint8Array(buffer);let byteString = "";for (let i = 0; i < byteArray.byteLength; i += 1) {byteString += String.fromCodePoint(byteArray[i]);}return byteString;}crypto.subtle.generateKey({name: "RSA-OAEP",hash: "SHA-256",modulusLength: 4096,publicExponent: new Uint8Array([1,0,1])}, true, ["encrypt", "decrypt"]).then((keysObject) => {crypto.subtle.exportKey("pkcs8", keysObject.privateKey).then(result => {const privateKey = btoa(arrayBufferToString(result));console.log(`Private key: `, privateKey);});crypto.subtle.exportKey("spki", keysObject.publicKey).then(result => {const publicKey = btoa(arrayBufferToString(result));console.log(`Public key: `,publicKey);});}); -
Paste the script in the Chrome console.
-
Press enter on the keyboard.
Option 2: With OpenSSL or OpenSSH
Section titled Option 2: With OpenSSL or OpenSSHOn linux/Mac, you can use OpenSSL:
openssl genpkey -out mykey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:4096openssl rsa -in mykey.pem -pubout > mykey.pub
On Windows you will need to install WSL.
How to add a public key
Section titled How to add a public key- Go to the Console > Choose the account > Choose the project > “Encryption management” tab.
- Click “Store public key”.
- Paste your preferred public key.
- Click “Store key”.
Unmask on-screen text in Contentsquare
Section titled Unmask on-screen text in Contentsquare- Select the key icon and select Unmask on-screen text
- Enter in the private key and select decrypt (all replays will be exposed).
User identifier
Section titled User identifierThe User Identifier feature allows searching for replays belonging to specific users, using a unique identifier. You gain the ability to search and access all sessions associated with a specific user, for example john@gmail.com
, within the selected date range. This is useful in the case of user feedback or complaints.
The user identifier can be any value which uniquely identifies a user such as a username, an account number, email address, phone number, CRM ID, or loyalty system ID. It is immediately hashed and encoded in a one-way format before it is sent to Contentsquare.
When filtering Session Replays with a user identifier in Contentsquare, the same hashing algorithm is used and both hashes are compared to match the user identifier with its associated replay(s).
User identifier implementation
Section titled User identifier implementation-
Choosing the right User Identifier: Opt for a unique identifier like the email address, username, or account number that is available on the front-end.
-
Collecting User Identifiers: To collect the user identifier, you need to send a page event using the
trackPageEvent
command, with the prefix@user-identifier@
:window._uxa = window._uxa || [];window._uxa.push(['trackPageEvent', '@user-identifier@user_email']);Or if you pull the identifier from the data layer:
window._uxa = window._uxa || [];window._uxa.push(['trackPageEvent', '@user-identifier@' + dataLayer.Name]);The user identifier value is limited to 100 characters.