Skip to content

Commit dbe8198

Browse files
Copilotcpholguera
andcommitted
Add setHideOverlayWindows to knowledge and best practices, fix demo structure
Co-authored-by: cpholguera <29175115+cpholguera@users.noreply.github.com>
1 parent 4005d31 commit dbe8198

File tree

4 files changed

+96
-166
lines changed

4 files changed

+96
-166
lines changed

best-practices/MASTG-BEST-0029.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ Implement touch filtering to prevent touch events when the app's UI is obscured
1616

1717
2. **Call `setFilterTouchesWhenObscured(true)`** programmatically on sensitive views to enable touch filtering at runtime.
1818

19-
3. **Override `onFilterTouchEventForSecurity`** for more granular control and to implement custom security policies based on your app's specific requirements.
19+
3. **Call `setHideOverlayWindows(true)`** on the window (API level 31+) to hide all non-system overlay windows while the activity is in the foreground. This provides stronger protection by preventing overlays entirely rather than just filtering touch events.
2020

21-
4. **Check motion event flags** such as `FLAG_WINDOW_IS_OBSCURED` (API level 9+) or `FLAG_WINDOW_IS_PARTIALLY_OBSCURED` (API level 29+) in touch event handlers to detect obscured windows and respond appropriately.
21+
4. **Override `onFilterTouchEventForSecurity`** for more granular control and to implement custom security policies based on your app's specific requirements.
22+
23+
5. **Check motion event flags** such as `FLAG_WINDOW_IS_OBSCURED` (API level 9+) or `FLAG_WINDOW_IS_PARTIALLY_OBSCURED` (API level 29+) in touch event handlers to detect obscured windows and respond appropriately.
2224

2325
Apply these protections selectively to security-sensitive UI elements where user confirmation is critical, such as:
2426

@@ -52,6 +54,7 @@ Touch filtering mechanisms help ensure that user interactions occur with the int
5254
- Android Developer Documentation: [Tapjacking](https://developer.android.com/privacy-and-security/risks/tapjacking)
5355
- Android Developer Documentation: [View Security](https://developer.android.com/reference/android/view/View#security)
5456
- Android Developer Documentation: [setFilterTouchesWhenObscured](https://developer.android.com/reference/android/view/View#setFilterTouchesWhenObscured(boolean))
57+
- Android Developer Documentation: [setHideOverlayWindows](https://developer.android.com/reference/android/view/Window#setHideOverlayWindows(boolean))
5558
- Android Developer Documentation: [onFilterTouchEventForSecurity](https://developer.android.com/reference/android/view/View#onFilterTouchEventForSecurity(android.view.MotionEvent))
5659
- Android Developer Documentation: [FLAG_WINDOW_IS_OBSCURED](https://developer.android.com/reference/android/view/MotionEvent#FLAG_WINDOW_IS_OBSCURED)
5760
- Android Developer Documentation: [FLAG_WINDOW_IS_PARTIALLY_OBSCURED](https://developer.android.com/reference/android/view/MotionEvent#FLAG_WINDOW_IS_PARTIALLY_OBSCURED)
Lines changed: 41 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,58 @@
11
package org.owasp.mastestapp
22

3-
import android.os.Bundle
3+
import android.content.Context
44
import android.view.MotionEvent
5-
import android.view.View
65
import android.widget.Button
7-
import android.widget.Toast
8-
import androidx.activity.ComponentActivity
9-
import androidx.activity.compose.setContent
10-
import androidx.activity.enableEdgeToEdge
11-
import androidx.compose.foundation.layout.Column
12-
import androidx.compose.foundation.layout.fillMaxWidth
13-
import androidx.compose.foundation.layout.padding
14-
import androidx.compose.material3.Button
15-
import androidx.compose.material3.Text
16-
import androidx.compose.runtime.Composable
17-
import androidx.compose.ui.Modifier
18-
import androidx.compose.ui.platform.ComposeView
19-
import androidx.compose.ui.unit.dp
20-
import androidx.compose.ui.viewinterop.AndroidView
6+
import android.widget.LinearLayout
217

228
// SUMMARY: This sample demonstrates different approaches to handling overlay attacks in Android apps,
239
// showing both vulnerable patterns and proper protections using filterTouchesWhenObscured.
2410

25-
const val MASTG_TEXT_TAG = "mastgTestText"
11+
class MastgTest (private val context: Context){
2612

27-
class MainActivity : ComponentActivity() {
28-
override fun onCreate(savedInstanceState: Bundle?) {
29-
super.onCreate(savedInstanceState)
30-
enableEdgeToEdge()
31-
setContent {
32-
MainScreen()
33-
}
34-
}
35-
}
13+
fun mastgTest(): String {
14+
val layout = LinearLayout(context)
15+
layout.orientation = LinearLayout.VERTICAL
3616

37-
@Composable
38-
fun MainScreen() {
39-
Column(modifier = Modifier.padding(16.dp)) {
40-
// FAIL: [MASTG-TEST-0035] Sensitive button without overlay protection
41-
Button(
42-
onClick = {
17+
// FAIL: [MASTG-TEST-0x35] Sensitive button without overlay protection
18+
val vulnerableButton = Button(context).apply {
19+
text = "Vulnerable: Confirm Payment"
20+
setOnClickListener {
4321
// Sensitive action: confirming a payment
44-
},
45-
modifier = Modifier
46-
.fillMaxWidth()
47-
.padding(vertical = 8.dp)
48-
) {
49-
Text("Vulnerable: Confirm Payment")
22+
}
5023
}
24+
layout.addView(vulnerableButton)
5125

52-
// PASS: [MASTG-TEST-0035] Button with overlay protection using filterTouchesWhenObscured
53-
AndroidView(
54-
factory = { context ->
55-
Button(context).apply {
56-
text = "Protected: Confirm Payment"
57-
filterTouchesWhenObscured = true
58-
setOnClickListener {
59-
// Sensitive action protected from overlay attacks
60-
Toast.makeText(context, "Payment confirmed", Toast.LENGTH_SHORT).show()
61-
}
62-
}
63-
},
64-
modifier = Modifier
65-
.fillMaxWidth()
66-
.padding(vertical = 8.dp)
67-
)
26+
// PASS: [MASTG-TEST-0x35] Button with overlay protection using filterTouchesWhenObscured
27+
val protectedButton = Button(context).apply {
28+
text = "Protected: Confirm Payment"
29+
filterTouchesWhenObscured = true
30+
setOnClickListener {
31+
// Sensitive action protected from overlay attacks
32+
}
33+
}
34+
layout.addView(protectedButton)
6835

69-
// PASS: [MASTG-TEST-0035] Custom view with manual obscured check
70-
AndroidView(
71-
factory = { context ->
72-
object : Button(context) {
73-
override fun onFilterTouchEventForSecurity(event: MotionEvent): Boolean {
74-
if ((event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
75-
// Window is obscured, filter the touch event
76-
Toast.makeText(context, "Touch blocked - window obscured", Toast.LENGTH_SHORT).show()
77-
return false
78-
}
79-
return super.onFilterTouchEventForSecurity(event)
80-
}
81-
}.apply {
82-
text = "Custom Protection: Grant Permission"
83-
setOnClickListener {
84-
// Sensitive permission grant protected by custom implementation
85-
Toast.makeText(context, "Permission granted", Toast.LENGTH_SHORT).show()
86-
}
36+
// PASS: [MASTG-TEST-0x35] Custom view with manual obscured check
37+
val customProtectedButton = object : Button(context) {
38+
override fun onFilterTouchEventForSecurity(event: MotionEvent): Boolean {
39+
if ((event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
40+
// Window is obscured, filter the touch event
41+
return false
8742
}
88-
},
89-
modifier = Modifier
90-
.fillMaxWidth()
91-
.padding(vertical = 8.dp)
92-
)
43+
return super.onFilterTouchEventForSecurity(event)
44+
}
45+
}.apply {
46+
text = "Custom Protection: Grant Permission"
47+
setOnClickListener {
48+
// Sensitive permission grant protected by custom implementation
49+
}
50+
}
51+
layout.addView(customProtectedButton)
52+
53+
return "Created buttons with various overlay protections:\n" +
54+
"1. Vulnerable button (no protection)\n" +
55+
"2. Protected button (filterTouchesWhenObscured)\n" +
56+
"3. Custom protected button (onFilterTouchEventForSecurity)"
9357
}
9458
}
Lines changed: 49 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,59 @@
11
package org.owasp.mastestapp;
22

3-
import android.os.Bundle;
3+
import android.content.Context;
44
import android.view.MotionEvent;
55
import android.widget.Button;
6-
import android.widget.Toast;
7-
import androidx.activity.ComponentActivity;
8-
import androidx.compose.foundation.layout.ColumnKt;
9-
import androidx.compose.foundation.layout.PaddingKt;
10-
import androidx.compose.material3.ButtonKt;
11-
import androidx.compose.material3.TextKt;
12-
import androidx.compose.runtime.Composer;
13-
import androidx.compose.ui.Modifier;
14-
import androidx.compose.ui.unit.Dp;
15-
import androidx.compose.ui.viewinterop.AndroidViewKt;
16-
import kotlin.jvm.functions.Function0;
17-
import kotlin.jvm.functions.Function1;
18-
import kotlin.jvm.functions.Function2;
19-
import kotlin.jvm.internal.Lambda;
6+
import android.widget.LinearLayout;
7+
import kotlin.jvm.internal.Intrinsics;
208

21-
public final class MainActivity extends ComponentActivity {
22-
public void onCreate(Bundle savedInstanceState) {
23-
super.onCreate(savedInstanceState);
24-
this.enableEdgeToEdge();
25-
this.setContent(new Lambda(0) {
26-
public final void invoke(Composer $composer, int $changed) {
27-
MainScreenKt.MainScreen($composer, 0);
28-
}
29-
});
9+
public final class MastgTest {
10+
private final Context context;
11+
12+
public MastgTest(Context context) {
13+
Intrinsics.checkNotNullParameter(context, "context");
14+
this.context = context;
3015
}
31-
}
3216

33-
public final class MainScreenKt {
34-
public static final void MainScreen(Composer $composer, int $changed) {
35-
Composer $composer2 = $composer.startRestartGroup(0);
36-
if ($changed == 0) {
37-
if (!$composer2.getSkipping()) {
38-
ColumnKt.m586Column(PaddingKt.m565padding(Modifier.Companion, Dp.m5307constructorimpl(16)), null, null, new Lambda(3) {
39-
public final void invoke(ColumnScope $this$Column, Composer $composer, int $changed) {
40-
Composer $composer2 = $composer;
41-
ColumnScope columnScope = $this$Column;
42-
43-
// FAIL: [MASTG-TEST-0035] Vulnerable button without overlay protection
44-
ButtonKt.m1334Button(new Lambda(0) {
45-
public final void invoke() {
46-
// Sensitive action: confirming a payment
47-
}
48-
}, PaddingKt.m565padding(Modifier.Companion.fillMaxWidth(), Dp.m5307constructorimpl(8)), false, null, null, null, null, null, new Lambda(3) {
49-
public final void invoke(RowScope $this$Button, Composer $composer, int $changed) {
50-
TextKt.m3574Text("Vulnerable: Confirm Payment", null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, $composer, 0, 0, 131070);
51-
}
52-
}, $composer2, 805306368, 508);
53-
54-
// PASS: [MASTG-TEST-0035] Button with overlay protection
55-
AndroidViewKt.m4555AndroidView(new Lambda(1) {
56-
public final Button invoke(Context context) {
57-
Button button = new Button(context);
58-
button.setText("Protected: Confirm Payment");
59-
button.setFilterTouchesWhenObscured(true);
60-
button.setOnClickListener(new View.OnClickListener() {
61-
public void onClick(View view) {
62-
Toast.makeText(context, "Payment confirmed", 0).show();
63-
}
64-
});
65-
return button;
66-
}
67-
}, PaddingKt.m565padding(Modifier.Companion.fillMaxWidth(), Dp.m5307constructorimpl(8)), null, null, $composer2, 3080, 12);
68-
69-
// PASS: [MASTG-TEST-0035] Custom view with manual obscured check
70-
AndroidViewKt.m4555AndroidView(new Lambda(1) {
71-
public final Button invoke(Context context) {
72-
return new Button(context) {
73-
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
74-
if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
75-
Toast.makeText(this.getContext(), "Touch blocked - window obscured", 0).show();
76-
return false;
77-
}
78-
return super.onFilterTouchEventForSecurity(event);
79-
}
80-
81-
{
82-
this.setText("Custom Protection: Grant Permission");
83-
this.setOnClickListener(new View.OnClickListener() {
84-
public void onClick(View view) {
85-
Toast.makeText(Button.this.getContext(), "Permission granted", 0).show();
86-
}
87-
});
88-
}
89-
};
90-
}
91-
}, PaddingKt.m565padding(Modifier.Companion.fillMaxWidth(), Dp.m5307constructorimpl(8)), null, null, $composer2, 3080, 12);
92-
}
93-
}, $composer2, 438, 6);
17+
public final String mastgTest() {
18+
LinearLayout layout = new LinearLayout(this.context);
19+
layout.setOrientation(1);
20+
21+
// FAIL: [MASTG-TEST-0x35] Sensitive button without overlay protection
22+
Button vulnerableButton = new Button(this.context);
23+
vulnerableButton.setText("Vulnerable: Confirm Payment");
24+
vulnerableButton.setOnClickListener(view -> {
25+
// Sensitive action: confirming a payment
26+
});
27+
layout.addView(vulnerableButton);
28+
29+
// PASS: [MASTG-TEST-0x35] Button with overlay protection using filterTouchesWhenObscured
30+
Button protectedButton = new Button(this.context);
31+
protectedButton.setText("Protected: Confirm Payment");
32+
protectedButton.setFilterTouchesWhenObscured(true);
33+
protectedButton.setOnClickListener(view -> {
34+
// Sensitive action protected from overlay attacks
35+
});
36+
layout.addView(protectedButton);
37+
38+
// PASS: [MASTG-TEST-0x35] Custom view with manual obscured check
39+
Button customProtectedButton = new Button(this.context) {
40+
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
41+
if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
42+
// Window is obscured, filter the touch event
43+
return false;
44+
}
45+
return super.onFilterTouchEventForSecurity(event);
9446
}
95-
}
47+
};
48+
customProtectedButton.setText("Custom Protection: Grant Permission");
49+
customProtectedButton.setOnClickListener(view -> {
50+
// Sensitive permission grant protected by custom implementation
51+
});
52+
layout.addView(customProtectedButton);
53+
54+
return "Created buttons with various overlay protections:\n" +
55+
"1. Vulnerable button (no protection)\n" +
56+
"2. Protected button (filterTouchesWhenObscured)\n" +
57+
"3. Custom protected button (onFilterTouchEventForSecurity)";
9658
}
9759
}

knowledge/android/MASVS-PLATFORM/MASTG-KNOW-0022.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Android provides several defensive mechanisms that apps can use to protect again
1616

1717
- [`onFilterTouchEventForSecurity`](https://developer.android.com/reference/android/view/View#onFilterTouchEventForSecurity(android.view.MotionEvent)): Override this method for fine-grained control to implement custom security policies for views.
1818
- [`android:filterTouchesWhenObscured`](https://developer.android.com/reference/android/view/View#attr_android:filterTouchesWhenObscured): Set this layout attribute to `true` or call [`setFilterTouchesWhenObscured`](https://developer.android.com/reference/android/view/View#setFilterTouchesWhenObscured(boolean)) to filter touch events when the view is obscured by another visible window.
19+
- [`setHideOverlayWindows`](https://developer.android.com/reference/android/view/Window#setHideOverlayWindows(boolean)) (since API level 31): Call this method on the window to hide all non-system overlay windows while the activity is in the foreground. This provides a stronger protection by preventing overlays entirely rather than just filtering touch events.
1920
- [`FLAG_WINDOW_IS_OBSCURED`](https://developer.android.com/reference/android/view/MotionEvent#FLAG_WINDOW_IS_OBSCURED) (since API level 9): Check this flag to detect if the window is obscured.
2021
- [`FLAG_WINDOW_IS_PARTIALLY_OBSCURED`](https://developer.android.com/reference/android/view/MotionEvent#FLAG_WINDOW_IS_PARTIALLY_OBSCURED) (since API level 29): Check this flag to detect if the window is partially obscured.
2122

0 commit comments

Comments
 (0)