Protect sensitive data
As with any third-party service, it’s important for you to understand and have the ability to manage what data is sent to Shake servers. Shake SDK allows you to filter out sensitive data on the mobile device itself, so it never reaches the Shake servers.
Automatically redacted sensitive data
Shake automatically redacts these sensitive data from your notifications, touch events and network requests:
- email addresses
- IP addresses
- credit card numbers
- bearer tokens
Shake also redacts network header values if the header key is:
- password
- secret
- passwd
- api_key
- apikey
- access_token
- auth_token
- credentials
- mysql_pwd
- stripetoken
- Authorization
- Proxy-Authorization
- card[number]
- token
To disable this privacy feature, use the method below:
- Java
- Kotlin
Shake.getReportConfiguration().setSensitiveDataRedactionEnabled(false);
Shake.getReportConfiguration().isSensitiveDataRedactionEnabled = false
Views
You can mark any view as private, and it'll automatically be deleted from the auto screenshot. Private views are stored as a weak reference. They get cleared from the memory when not used anymore.
These methods won't delete sensitive views from auto screen recording — only from the auto screenshot.
Let's suppose you're building a shopping app and you want to delete the name and the credit card number views from the auto screenshot:
- Java
- Kotlin
import android.view.TextView;import com.shakebugs.shake.Shake;private void maskSensitiveData() {TextView textName = (TextView) findViewById(R.id.text_name);TextView textCardNumber = (TextView) findViewById(R.id.text_card_number);Shake.addPrivateView(textName);Shake.addPrivateView(textCardNumber);}
import com.shakebugs.shake.Shakeimport kotlinx.android.synthetic.main.activity_payment.*private fun maskSensitiveData() {Shake.addPrivateView(textName)Shake.addPrivateView(textCardNumber)}
To remove a view from private views use the following method:
- Java
- Kotlin
Shake.removePrivateView(view);
Shake.removePrivateView(view)
If you want to delete an entire screen from the auto screenshot, simply mark the whole activity as private:
- Java
- Kotlin
import android.os.Bundle;import android.view.TextView;import androidx.appcompat.app.AppCompatActivity;import com.shakebugs.shake.Shake;public class PaymentActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_payment);Shake.addPrivateView(this);}...}
import android.os.Bundle;import androidx.appcompat.app.AppCompatActivityimport com.shakebugs.shake.Shakepublic class PaymentActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_payment)Shake.addPrivateView(this)}...}
To remove an activity from the list of private views, use the following method:
- Java
- Kotlin
Shake.removePrivateView(activity);
Shake.removePrivateView(activity)
To clear all the private views, use the following method:
- Java
- Kotlin
Shake.clearPrivateViews();
Shake.clearPrivateViews()
Auto screen recording
Use the Android system flag FLAG_SECURE
on an activity if you want to prevent
sensitive data from being visible on the auto screen recording.
It will make the activity black in the video:
- Java
- Kotlin
import android.os.Bundle;import android.view.WindowManager;import androidx.appcompat.app.AppCompatActivity;public class PaymentActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_payment);getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,WindowManager.LayoutParams.FLAG_SECURE);}...}
import android.os.Bundleimport android.view.WindowManagerimport androidx.appcompat.app.AppCompatActivitypublic class PaymentActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_payment)window.setFlags(WindowManager.LayoutParams.FLAG_SECURE,WindowManager.LayoutParams.FLAG_SECURE)}...}
Visit Auto screen recording to read how to turn it off altogether.
Jetpack Compose
In Jetpack Compose, the main building block of the UI is Composeable instead of View.
If you want to make a specific Composeable private, copy the following snippet into your project:
import androidx.compose.runtime.Composableimport androidx.compose.ui.platform.ComposeViewimport androidx.compose.ui.viewinterop.AndroidViewimport com.shakebugs.shake.Shake@Composablefun ShakePrivateView(content: @Composable () -> Unit) {AndroidView(factory = { context ->ComposeView(context).apply {Shake.addPrivateView(this)setContent { content() }}})}
Use ShakePrivateView
Composeable as shown below:
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeTestTheme {Surface(color = MaterialTheme.colors.background) {ShakePrivateView {Greeting("Android")}}}}}}
In the example above, the Greeting component will be removed from the screenshots.
Touch events
Marking a view as private will automatically delete its touch events' text properties too.
Consequently, you'll see them as data_redacted
strings in ticket's
Activity history.
The view's ID, accessibility labels and tags remain visible.
Network requests
Network requests may contain sensitive data which you may not want to send to Shake servers.
Use the Shake.setNetworkRequestsFilter()
method to obfuscate sensitive parts of those requests,
or to entirely prevent certain network requests from being logged.
As an example, if you'd like to obfuscate the Authorization header in all network requests sent from your app, do this:
- Java
- Kotlin
import com.shakebugs.shake.Shake;import com.shakebugs.shake.privacy.NetworkRequestEditor;import com.shakebugs.shake.privacy.NetworkRequestsFilter;private void setupNetworkFilter() {Shake.setNetworkRequestsFilter(new NetworkRequestsFilter() {@Overridepublic NetworkRequestEditor filter(NetworkRequestEditor networkRequest) {Map<String, String> headers = networkRequest.getRequestHeaders();if (headers.containsKey("Authorization")) {headers.put("Authorization", "***");}return networkRequest;}});}
import com.shakebugs.shake.Shakeprivate fun setupNetworkFilter() {Shake.setNetworkRequestsFilter { networkRequest ->val headers = networkRequest.requestHeadersif (headers.containsKey("Authorization")) {headers["Authorization"] = "***"}networkRequest}}
If you don't want to log specific network requests, return null
from the NetworkRequestsFilter
as shown below:
- Java
- Kotlin
import com.shakebugs.shake.Shake;import com.shakebugs.shake.privacy.NetworkRequestEditor;import com.shakebugs.shake.privacy.NetworkRequestsFilter;private void setupNetworkFilter() {Shake.setNetworkRequestsFilter(new NetworkRequestsFilter() {@Overridepublic NetworkRequestEditor filter(NetworkRequestEditor networkRequest) {if (networkRequest.getUrl().startsWith("https://api.myapp.com/cards")) {return null;}return networkRequest;}});}
import com.shakebugs.shake.Shakeprivate fun setupNetworkFilter() {Shake.setNetworkRequestsFilter { networkRequest ->if (networkRequest.url.startsWith("https://api.myapp.com/cards")) {null} else networkRequest}}
To clear the network requests filter, use Shake.setNetworkRequestsFilter(null)
.
Notification events
If your app notifications contain sensitive data, use the Shake.setNotificationEventsFilter()
method to fully or partially obfuscate those notifications.
For example, if you'd like to obfuscate the description of the notification event that contains an email, do this:
- Java
- Kotlin
import com.shakebugs.shake.Shake;import com.shakebugs.shake.privacy.NotificationEventEditor;import com.shakebugs.shake.privacy.NotificationEventsFilter;private void setupNotificationsFilter() {Shake.setNotificationEventsFilter(new NotificationEventsFilter() {@Overridepublic NotificationEventEditor filter(NotificationEventEditor notificationEvent) {if (notificationEvent.getTitle().equals("E-mail changed")) {notificationEvent.setDescription("***@gmail.com");}return notificationEvent;}});}
import com.shakebugs.shake.Shakeimport com.shakebugs.shake.privacy.NotificationEventEditorimport com.shakebugs.shake.privacy.NotificationEventsFilterprivate fun setupNotificationsFilter() {Shake.setNotificationEventsFilter { notificationEvent ->if (notificationEvent.title == "E-mail changed") {notificationEvent.description = "***@gmail.com"}notificationEvent}}
If you do not want to track a specific notification event, return null
from the NotificationEventsFilter
like below:
- Java
- Kotlin
import com.shakebugs.shake.Shake;import com.shakebugs.shake.privacy.NetworkRequestEditor;import com.shakebugs.shake.privacy.NetworkRequestsFilter;private void setupNotificationsFilter() {Shake.setNotificationEventsFilter(new NotificationEventsFilter() {@Overridepublic NotificationEventEditor filter(NotificationEventEditor notificationEvent) {if (notificationEvent.getTitle().equals("E-mail changed")) {return null;}return notificationEvent;}});}
import com.shakebugs.shake.Shakeprivate fun setupNotificationsFilter() {Shake.setNotificationEventsFilter { notificationEvent ->if (notificationEvent.title == "E-mail changed") {null} else notificationEvent}}
To clear the notification events filter, use Shake.setNotificationEventsFilter(null)
.