Overview

Session Replay provides privacy controls to ensure organizations of any scale do not expose sensitive or personal data. Data is stored on Datadog-managed cloud instances and encrypted at rest.

Default privacy options for Session Replay protect end user privacy and prevent sensitive organizational information from being collected.

By enabling Mobile Session Replay, you can automatically mask sensitive elements from being recorded through the RUM Mobile SDK. When data is masked, that data is not collected in its original form by Datadog’s SDKs and thus is not sent to the backend.

Configuring masking modes

Fine-Grained Masking

Using the masking modes below, you can override the default setup on a per-application basis. Masking is fine-grained, which means you can override masking for text and inputs, images, and touches individually to create a custom configuration that suits your needs.

Text and input masking

By default, the mask_all setting is enabled for all data. With this setting enabled, all text and input content on screen is masked, as shown below.

What your application screen may resemble when `mask` is enabled.

Mask sensitive inputs

With the mask_sensitive_inputs setting enabled, all text and inputs are shown except those considered sensitive, such as password fields.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setTextAndInputPrivacy(TextAndInputPrivacy.MASK_SENSITIVE_INPUTS)
    .build()
    SessionReplay.enable(sessionReplayConfig)

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: .maskSensitiveInputs,
        imagePrivacyLevel: imagePrivacyLevel,
        touchPrivacyLevel: touchPrivacyLevel
    )
    SessionReplay.enable(with: sessionReplayConfig)

Mask all inputs

With the mask_all_inputs setting enabled, all inputs fields are masked in the replay.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setTextAndInputPrivacy(TextAndInputPrivacy.MASK_ALL_INPUTS)
    .build()
    SessionReplay.enable(sessionReplayConfig)

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: .maskAllInputs,
        imagePrivacyLevel: imagePrivacyLevel,
        touchPrivacyLevel: touchPrivacyLevel
    )
    SessionReplay.enable(with: sessionReplayConfig)

Mask all

With the mask_all setting enabled, all text and input fields are masked in the replay.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setTextAndInputPrivacy(TextAndInputPrivacy.MASK_ALL)
    .build()
    SessionReplay.enable(sessionReplayConfig)

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: .maskAll,
        imagePrivacyLevel: imagePrivacyLevel,
        touchPrivacyLevel: touchPrivacyLevel
    )
    SessionReplay.enable(with: sessionReplayConfig)

Image masking

By default, the mask_all setting is enabled for all images. With this setting enabled, all images on screen are masked.

Mask all images

With the mask_all setting enabled, all images are replaced by placeholders labeled ‘Image’ in the replay.

What your application screen may resemble when `mask-all` is enabled.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setImagePrivacy(ImagePrivacy.MASK_ALL)
    .build()
    SessionReplay.enable(sessionReplayConfig)

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: textAndInputPrivacyLevel,
        imagePrivacyLevel: .maskAll,
        touchPrivacyLevel: touchPrivacyLevel
    )
    SessionReplay.enable(with: sessionReplayConfig)

Mask content images

To manage content masking while still showing system images, users can choose the following options:

On iOS, users can select the mask_non_bundled_only setting, which replaces any image that is not part of the system with a “Content Image” placeholder.

On Android, users can select the mask_large_only setting, which replaces images with dimensions that exceed 100x100dp with a “Content Image” placeholder.

Note: These dimensions refer to the drawable resource, not the view’s size.

What your application screen may resemble when `mask_large_only` is enabled on Android.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setImagePrivacy(ImagePrivacy.MASK_LARGE_ONLY)
    .build()
    SessionReplay.enable(sessionReplayConfig)
What your application screen may resemble when `mask_non_bundled_only` is enabled on iOS.

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: textAndInputPrivacyLevel,
        imagePrivacyLevel: .maskNonBundledOnly,
        touchPrivacyLevel: touchPrivacyLevel
    )
    SessionReplay.enable(with: sessionReplayConfig)

Show all images

With the mask_none setting enabled, all images are shown in the replay.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setImagePrivacy(ImagePrivacy.MASK_NONE)
    .build()
    SessionReplay.enable(sessionReplayConfig)

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: textAndInputPrivacyLevel,
        imagePrivacyLevel: .maskNone,
        touchPrivacyLevel: touchPrivacyLevel
    )
    SessionReplay.enable(with: sessionReplayConfig)

Touch masking

By default, the hide setting is enabled for all touches. With this setting enabled, all touches on screen are hidden.

Hide all touches

With the hide setting enabled, all touches that occur during the replay are hidden. This is the default setting.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setTouchPrivacy(TouchPrivacy.HIDE)
    .build()
    SessionReplay.enable(sessionReplayConfig)

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: textAndInputPrivacyLevel,
        imagePrivacyLevel: imagePrivacyLevel,
        touchPrivacyLevel: .hide
    )
    SessionReplay.enable(with: sessionReplayConfig)

Show all touches

With the show setting enabled, all touches that occur during the replay are shown.

application.kt

    val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
    .setTouchPrivacy(TouchPrivacy.SHOW)
    .build()
    SessionReplay.enable(sessionReplayConfig)

AppDelegate.swift

    let sessionReplayConfig = SessionReplay.Configuration(
        replaySampleRate: sampleRate, 
        textAndInputPrivacyLevel: textAndInputPrivacyLevel,
        imagePrivacyLevel: imagePrivacyLevel,
        touchPrivacyLevel: .show
    )
    SessionReplay.enable(with: sessionReplayConfig)

Legacy masking - Deprecated

This masking API is deprecated. Users are encouraged to migrate to the fine-grained masking options described above.

Mask all text elements

By default, the mask setting is enabled for all data. With this setting enabled, all text content on screen is masked, touches are hidden and images are replaced by placeholders, as shown below.

What your application screen may resemble when `mask` is enabled.

application.kt

   // mask all text elements
   val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
       .setPrivacy(SessionReplayPrivacy.MASK)
       .build()
   SessionReplay.enable(sessionReplayConfig)
   

AppDelegate.swift

    // mask all text elements
    SessionReplay.enable(
        with: SessionReplay.Configuration(
            replaySampleRate: sampleRate,
            defaultPrivacyLevel: .mask
        )
    )

   

Mask only input elements

With the mask user input setting enabled, any input field is replaced with anonymized text.

Note: In addition to this behavior, touches are hidden, and some images (>100x100dp images on android/non-system images on ios) are replaced with placeholders.

What your application screen may resemble when user input fields are masked.

build.gradle

   // mask only input elements
   val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
       .setPrivacy(SessionReplayPrivacy.MASK_USER_INPUT)
       .build()
   SessionReplay.enable(sessionReplayConfig)
   

AppDelegate.swift

   
   // mask only input elements
    SessionReplay.enable(
        with: SessionReplay.Configuration(
            replaySampleRate: sampleRate,
            defaultPrivacyLevel: .maskUserInput
        )
    )

   

Allow (no masking)

With the allow setting enabled, all text is revealed.

What your application screen may resemble when `allow` is enabled.

Note: Even with this option enabled, any sensitive text fields, such as passwords, emails, phone numbers, and addresses are still masked. For more information, see Text masking definitions.

build.gradle

   // no masking; all text is revealed
   val sessionReplayConfig = SessionReplayConfiguration.Builder([sampleRate])
      .setPrivacy(SessionReplayPrivacy.ALLOW)
      .build()
   SessionReplay.enable(sessionReplayConfig)
   

AppDelegate.swift

   // no masking; all text is revealed
    SessionReplay.enable(
        with: SessionReplay.Configuration(
            replaySampleRate: sampleRate,
            defaultPrivacyLevel: .allow
        )
    )

   

How and what data is masked

This section describes how the Datadog recorder handles masking based on data type and how that data is defined.

Text masking strategies

Depending on how you’ve configured your privacy settings, the type of text, and sensitivity of data, Datadog’s masking rules apply different strategies to different types of text fields.

Text masking strategyDescriptionExample
No maskThe text is revealed in the session replay"Hello world""Hello world"
Space-preserving maskEach visible character is replaced with a lowercase “x”"Hello world""xxxxx xxxxx"
Fixed-length maskThe entire text field is replaced with a constant of three asterisks (***)"Hello world""***"

With the above text strategies in mind, you have a few different options if you want to override the default privacy rule of mask in your configuration.

The following chart shows how Datadog applies different text masking strategies, using the rules you set up in your configuration, to the below text types.

TypeAllow allMask allMask user input
Sensitive textFixed-length maskFixed-length maskFixed-length mask
Input and option textNo maskFixed-length maskFixed-length mask
Static textNo maskSpace-preserving maskNo mask
Hint textNo maskFixed-length maskNo mask

Text masking definitions

Find below a description of how Datadog’s recorder treats each text type.

Sensitive text

Sensitive text includes passwords, e-mails, and phone numbers marked in a platform-specific way, and other forms of sensitivity in text available to each platform.

This includes passwords, e-mails and phone numbers in:

  • Text Field (iOS)
  • Text View (iOS)
  • Edit Text (Android)
  • Address information (iOS + Android)
  • Credit card numbers (iOS)
  • One-time codes (iOS)

Input and option text

Input and option text is text entered by the user with a keyboard or other text-input device, or a custom (non-generic) value in selection elements.

This includes the below.

  • User-entered text in:
    • Text Field (iOS)
    • Text View (iOS)
    • Edit Text (Android)
  • User-selected options in:
    • Value Picker (iOS + Android)
    • Segment (iOS)
    • Drop Down List (Android)
  • Notable exclusions:
    • Placeholder (hint) texts in Text Field, Text View and Edit Text (not entered by the user)
    • Non-editable texts in Text View (iOS).
    • Month, day, and year labels in Date Picker (generic values)

Static text

Static text is any text that is not directly entered by the user. This includes the below.

All texts in:

  • Checkbox and Radio Button titles (Android)
  • Texts in non-editable Text View (iOS)
  • Month, day and year labels in the date and time picker
  • Values updated in response to gesture interaction with input elements, such as the current value of the Slider
  • Other controls, not considered as “user input elements”, such as Labels, Tab Bar, and Navigation Bar (iOS), or Tabs (Android)

Hint text

Hint text is static text in editable text elements or option selectors that is displayed when no value is given. This includes:

  • Placeholders in Text Field (iOS), Text View (iOS)
  • Hints in Edit Text (Android)
  • Prompts in Drop Down lists (Android)

Appearance masking

The following chart shows how we apply different appearance masking strategies, using the rules you set up in your configuration, to the below text types.

TypeAllow allMask allMask user input
Revealing attributes
Other attributes

Revealing attributes

Revealing attributes are attributes that can reveal or suggest the value of input elements and can be used to infer a user’s input or selection.

This includes:

Shapes

  • Background of selected option in Segment (iOS)
  • Circle surrounding the selected date in a Date Picker (iOS)
  • Selection mark in Checkbox (Android)
  • Thumb of a Slider (iOS and Android)

Text attributes

  • The color of a label rendering the selected date in Date Picker (iOS)
  • The position of the first and last option in Value Picker (iOS and Android)

Touch interactions

The following chart shows how we apply different touch interaction strategies, using the rules you set up in your configuration, to the below text types. While any interaction that happens on an on-screen keyboard is masked, interactions with other elements are not masked.

TypeAllow allMask allMask user input
Other attributes
On-screen keyboard

Image masking

The following chart shows how we apply different image masking strategies:

TypeMask NoneMark Large Only (Android)
/ Mask Non Bundled Only (iOS)
Mask All
Content ImageShownMaskedMasked
System ImageShownShownMasked

Further reading

PREVIEWING: rtrieu/product-analytics-ui-changes