Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions demos/android/MASVS-STORAGE/MASTG-DEMO-0070/MASTG-DEMO-0070.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ title: Sensitive Data Stored Unencrypted via Room Database
id: MASTG-DEMO-0070
code: [kotlin]
test: MASTG-TEST-0306
status: placeholder
note: This placeholder reserves the ID for a demo illustrating the insecure storage of sensitive data using the Android Room Persistence Library without integrating an external encryption solution (like SQLCipher). It directly corresponds to the MASTG-TEST-0306.
status: new
---

### Sample

The snippet below shows sample code that uses the Android Room Persistence Library to store sensitive data, including PII (email) and secrets (access token), in **plaintext** without any encryption.

{{ MastgTest.kt }}

### Steps

1. Install the app on a device (@MASTG-TECH-0005)
2. Make sure you have @MASTG-TOOL-0004 installed on your machine
3. Click the **Start** button
4. Execute `run.sh`.

The script pulls the Room database (`PrivateUnencryptedRoomDB`) along with its WAL/SHM files and queries the `users` table content:

{{ run.sh }}

### Observation

The output contains the extracted content from the `users` table, showing the sensitive PII (email address) and the access token stored in **plaintext**.

{{ output.txt }}

### Evaluation

This test fails because the app uses Room without additional encryption, storing sensitive data such as an access token (secret) and the user's email address (PII) in its sandbox in **plaintext**.
101 changes: 101 additions & 0 deletions demos/android/MASVS-STORAGE/MASTG-DEMO-0070/MastgTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.owasp.mastestapp

import android.content.Context
import android.util.Log
import androidx.room.*
import java.io.File

@Entity(tableName = "users")
data class UserEntity(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val username: String,
val email: String,
val token: String
)

@Dao
interface UserDao {
@Insert
fun insert(user: UserEntity)

@Query("DELETE FROM users")
fun clear()

@Query("SELECT * FROM users")
fun getAll(): List<UserEntity>
}

@Database(entities = [UserEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao

companion object {
fun getInstance(ctx: Context): AppDatabase {
val dbPath = ctx.getDatabasePath("PrivateUnencryptedRoomDB")
Log.i("MASTG-ROOM", "Database absolute path: ${dbPath.absolutePath}")

// Wymuś utworzenie katalogu databases jeśli nie istnieje
dbPath.parentFile?.let {
if (!it.exists()) {
Log.i("MASTG-ROOM", "Creating database directory: ${it.absolutePath}")
it.mkdirs()
} else {
Log.i("MASTG-ROOM", "Database directory exists: ${it.absolutePath}")
}
}

return Room.databaseBuilder(
ctx,
AppDatabase::class.java,
"PrivateUnencryptedRoomDB"
)
.allowMainThreadQueries()
.build()
}
}
}

class MastgTest(private val context: Context) {

private fun writeSensitiveDataToUnencryptedRoom() {
val username = "demoUser_room"
val userEmail = "[email protected]"
val accessToken = "ghp_1234567890abcdefghijklmnopqrstuvABCD"

try {
val db = AppDatabase.getInstance(context)
val dao = db.userDao()

Log.i("MASTG-ROOM", "Clearing existing users...")
dao.clear()

Log.i("MASTG-ROOM", "Inserting new user...")
dao.insert(
UserEntity(
username = username,
email = userEmail,
token = accessToken
)
)

val allUsers = dao.getAll()
Log.i("MASTG-ROOM", "Users in DB after insert: ${allUsers.size}")
allUsers.forEach {
Log.i("MASTG-ROOM", "User: ${it.username}, ${it.email}, ${it.token}")
}

Log.i("MASTG-ROOM", "Room DB written successfully: PrivateUnencryptedRoomDB")

} catch (e: Exception) {
Log.e("MASTG-ROOM", "Error writing Room DB: ${e.message}")
}
}

fun mastgTest(): String {
Log.i("MASTG-ROOM", "Starting MastgTest RoomDB demo...")
writeSensitiveDataToUnencryptedRoom()
val dbPath = context.getDatabasePath("PrivateUnencryptedRoomDB")
Log.i("MASTG-ROOM", "Database final path: ${dbPath.absolutePath}")
return "RoomDB Demo Complete. Plaintext PII + Token stored in unencrypted Room database."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.owasp.mastestapp;

import android.content.Context;
import android.util.Log;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;

/* compiled from: MastgTest.kt */
@Metadata(d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u0006\u0010\u0006\u001a\u00020\u0007R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\b"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48)
/* loaded from: classes3.dex */
public final class MastgTest {
public static final int $stable = 8;
private final Context context;

public MastgTest(Context context) {
Intrinsics.checkNotNullParameter(context, "context");
this.context = context;
}

public final String mastgTest() throws Exception {
DemoResults r = new DemoResults("0000");
try {
Log.d("MASTG-TEST", "Hello from the OWASP MASTG Test app.");
r.add(Status.PASS, "The app implemented a demo which passed the test with the following value: 'Hello from the OWASP MASTG Test app.'");
r.add(Status.FAIL, "The app implemented a demo which failed the test.");
throw new Exception("Example exception: Method not implemented.");
} catch (Exception e) {
r.add(Status.ERROR, e.toString());
return r.toJson();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
kapt("androidx.room:room-compiler:2.6.1")

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kotlin("kapt")
2 changes: 2 additions & 0 deletions demos/android/MASVS-STORAGE/MASTG-DEMO-0070/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--- Users Table Content ---
2|demoUser_room|[email protected]|ghp_1234567890abcdefghijklmnopqrstuvABCD
30 changes: 30 additions & 0 deletions demos/android/MASVS-STORAGE/MASTG-DEMO-0070/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

PACKAGE="org.owasp.mastestapp"
DB_NAME="PrivateUnencryptedRoomDB"
DB_PATH="/data/data/$PACKAGE/databases"
SDCARD_DIR="/sdcard/$PACKAGE-db"
OUTPUT_TXT_FILE="output.txt"

echo "[*] Pulling RoomDB (WAL mode)..."

adb shell "rm -rf $SDCARD_DIR && mkdir -p $SDCARD_DIR"

for f in "$DB_NAME" "$DB_NAME-wal" "$DB_NAME-shm"; do
if adb shell "run-as $PACKAGE cp $DB_PATH/$f $SDCARD_DIR/"; then
echo "[*] run-as succeeded for $f"
else
echo "[*] run-as failed for $f, trying su"
adb shell "su 0 sh -c 'cp $DB_PATH/$f $SDCARD_DIR/'"
fi
done

adb pull "$SDCARD_DIR/." .

echo "--- Users Table Content ---" > "$OUTPUT_TXT_FILE"
sqlite3 "$DB_NAME" "SELECT * FROM users;" >> "$OUTPUT_TXT_FILE"

adb shell "rm -rf $SDCARD_DIR" 2>/dev/null
rm "$DB_NAME" "$DB_NAME-wal" "$DB_NAME-shm" 2>/dev/null

echo "[*] Output written to $OUTPUT_TXT_FILE"
37 changes: 31 additions & 6 deletions tests-beta/android/MASVS-STORAGE/MASTG-TEST-0306.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
---
title: Sensitive Data Stored Unencrypted via Android Room DB
platform: android
title: Static Analysis for Unencrypted Sensitive Data in Android Room DB
id: MASTG-TEST-0306
type: [static, dynamic]
type: [static]
weakness: MASWE-0006
best-practices: []
profiles: [L1, L2]
status: placeholder
note: This test checks if the app uses the Android Room Persistence Library to store sensitive data (e.g., tokens, PII) without integrating an encryption layer (e.g., SQLCipher). It confirms the database file is stored in plaintext within the app's private sandbox.
best-practices: [MASTG-BEST-0025]
profiles: [L2]
status: new
---

## Overview

This test verifies whether the app's code uses the Android Room Persistence Library to store sensitive data — such as tokens, credentials, or PII — without encryption. By default, Room stores data in unencrypted SQLite databases.

## Steps

1. Obtain the application package (APK) using @MASTG-TECH-0003.

2. Use static analysis (@MASTG-TECH-0014) to identify references to Room APIs:
- `androidx.room.Room`
- `@Database`, `@Dao`, `@Entity` annotations
- `databaseBuilder`, `build` calls, `SupportSQLiteOpenHelper.Factory` implementations

3. Inspect whether:
- sensitive fields (tokens, secrets, PII) are stored in plaintext within `@Entity` classes
- a secure factory or wrapper (e.g., SQLCipher implementation of `SupportSQLiteOpenHelper.Factory`) is explicitly applied to the database builder

## Observation

- Which Room database files are referenced in the code
- Whether sensitive data is stored without encryption

## Evaluation

The test fails if the app stores sensitive data in Room databases without encryption (e.g., SQLCipher or equivalent) applied in the builder.
39 changes: 39 additions & 0 deletions tests-beta/android/MASVS-STORAGE/MASTG-TEST-0309.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
platform: android
title: Runtime Verification of Sensitive Data Stored Unencrypted in Android Room DB
id: MASTG-TEST-0309
type: [dynamic, filesystem]
weakness: MASWE-0006
best-practices: [MASTG-BEST-0025]
profiles: [L2]
status: new
---

## Overview

This test checks at runtime whether sensitive data — tokens, secrets, or PII — is stored in Room databases without encryption. The goal is to ensure that sensitive information is not persisted in plaintext within the app's private storage.

## Steps

1. Install and run the app on a rooted or emulated device (@MASTG-TECH-0005).

2. Trigger app functionality that processes or stores sensitive data.

3. Access the app's private storage and locate Room database files:
- `/data/data/<package_name>/databases/<database_name>`
- `/data/data/<package_name>/databases/<database_name>-wal`
- `/data/data/<package_name>/databases/<database_name>-shm`
(@MASTG-TECH-0008)

4. Extract the database files to the host machine using @MASTG-TECH-0003.

5. Inspect database contents using a SQLite client or dynamic analysis tool (@MASTG-TECH-0015) to confirm whether sensitive data is stored in plaintext.

## Observation

- Which Room database files exist on the device
- Whether sensitive data (tokens, secrets, PII) is stored in plaintext

## Evaluation

The test fails if sensitive data in Room database files can be read in plaintext and no encryption mechanism (e.g., SQLCipher) is applied.