Privacy
Learn how to mask parts of your app's data in Session Replay.
Before publising an App with Session Replay enabled, make sure to test it thoroughly to ensure that no sensitive data is exposed.
If you choose to use custom masking in your Session Replays, you may accidentally expose sensitive customer data. Be sure to double-check what you choose to expose.
By default, our Session Replay SDK masks all text content, images, webviews and user input. This helps ensure that no sensitive data will be exposed. You can also manually choose which parts of your app's data you want to mask by using the different options listed below.
To disable the default masking behavior (not to be used on applications with sensitive data):
options.experimental.sessionReplay.maskAllText = false
options.experimental.sessionReplay.maskAllImages = false
Session Replay Unmasked | Session Replay Masked |
---|---|
Make sure your Sentry Android SDK version is at least 7.15.0.
You can choose which type of view you want to mask or unmask by using the addMaskViewClass
or addUnmaskViewClass
options.
Let's say you have:
- A custom view that you want to mask
- A
TextView
subclass (which normally would be masked) that you don't want to mask
You can set the options like this:
options.experimental.sessionReplay.addMaskViewClass("com.example.MyCustomView")
options.experimental.sessionReplay.addUnmaskViewClass("com.example.MyCustomTextView")
If you're using a code obfuscation tool (R8/ProGuard), adjust your proguard rules accordingly so your custom view class names don't get minified.
The masking behavior applies to classes and their subclasses. This means if you add a view via addMaskViewClass
(for example, TextView
, which is the default behavior), its respective subclasses (RadioButton
, CheckBox
, EditText
, and so on) will also be masked. For example, you can do the following:
options.experimental.sessionReplay.addMaskViewClass("android.widget.TextView") // mask TextView and all its subclasses
options.experimental.sessionReplay.addUnmaskViewClass("android.widget.RadioButton") // but unmask RadioButton and all its subclasses
You can also choose to mask or unmask a specific view instance by using tags like this:
<View
android:id="@+id/my_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="sentry-mask|sentry-unmask"
/>
If your view already has a tag assigned, you can set the masking tag by a sentry-specific id:
<View
android:id="@+id/my_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<tag android:id="@id/sentry_privacy" android:value="mask|unmask" />
</View>
We also provide convenient extension functions for Kotlin:
view.sentryReplayMask()
// or
view.sentryReplayUnmask()
We only support masking specific composables in Jetpack Compose. Since composables don't have a concept of classes (they are all composable functions), masking by view class is not supported.
In the below example, we want the "Hello" message to be captured in the replay, but not the custom composable. (By default, all text composables are masked.)
import io.sentry.android.replay.sentryReplayMask
import io.sentry.android.replay.sentryReplayUnmask
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
MyCustomComposable(
modifier = Modifier.fillMaxWidth().sentryReplayMask()
...
)
Text("Hello", modifier = Modifier.sentryReplayUnmask())
}
Currently, we don't support masking anything within embedded Android views (AndroidView
), but you can still mask the entire view as follows:
import io.sentry.android.replay.sentryReplayMask
AndroidView(
modifier = Modifier.sentryReplayMask(),
factory = { context -> ... }
)
Here are some general rules we follow when applying masking rules:
When a
ViewGroup
is marked as masked, all its child views inherit this behavior and will be masked too, even if some of those child views would typically be ignored. This approach prioritizes safety and ensures no sensitive information is exposed unintentionally.When a
ViewGroup
is marked as unmasked, its child views do not automatically inherit its behavior. Each child view must be explicitly marked as unmasked if you want to capture them in the replay.
The following order determines how masking/unmasking rules are applied:
- Check if a view is marked as
unmasked
via a tag/extension function/modifier - Check if a view is marked as
masked
via a tag/extension function/modifier - Check if a view's class is marked as unmasked via
addUnmaskViewClass
- Check if a view's class is marked as masked via
addMaskViewClass
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").