MODE_WORLD_READABLEallows all applications to access and read the contents of
Please note that
MODE_WORLD_WRITEABLEwere deprecated starting on API level 17. Although newer devices may not be affected by this, applications compiled with an
android:targetSdkVersionvalue less than 17 may be affected if they run on an OS version that was released before Android 4.2 (API level 17).
.dbfiles. The Android SDK has built-in support for SQLite databases. The main package used to manage the databases is
android.database.sqlite. For example, you may use the following code to store sensitive information within an activity:
privateNotSoSecurewill be created with the provided data and stored in the clear text file
MODE_PRIVATE. Modes such as
MODE_WORLD_WRITEABLE(deprecated) may pose a security risk.
FileInputStreamto find out which files are opened and read within the app.
data/data/<package-name>/) will not be deleted when the user uninstalls the application. Finally, it's worth noting that the external storage can be used by an attacker to allow for arbitrary control of the application in some cases. For more information: see the blog from Checkpoint.
isInsideSecureHardwaremethod, which is part of the
Note that the relevant KeyInfo indicates that secret keys and HMAC keys are insecurely stored on several devices despite private keys being correctly stored on the secure hardware.
/data/misc/keystore/. Because the user's lock screen pin/password is used to generate the master key, the Android KeyStore is unavailable when the device is locked. For more security Android 9 (API level 28) introduces the
unlockedDeviceRequiedflag. By passing
setUnlockedDeviceRequiredmethod the app prevents its keys stored in
AndroidKeystorefrom being decrypted when the device is locked, and it requires the screen to be unlocked before allowing decryption.
true). Devices running Android 9 (API level 28) and higher can have a
StrongBox Keymastermodule, an implementation of the Keymaster HAL that resides in a hardware security module which has its own CPU, Secure storage, a true random number generator and a mechanism to resist package tampering. To use this feature,
truemust be passed to the
setIsStrongBoxBackedmethod in either the
KeyGenParameterSpec.Builderclass or the
KeyProtection.Builderclass when generating or importing keys using
AndroidKeystore. To make sure that StrongBox is used during runtime, check that
trueand that the system does not throw
StrongBoxUnavailableExceptionwhich gets thrown if the StrongBox Keymaster isn't available for the given algorithm and key size associated with a key. Description of features on hardware-based keystore can be found on AOSP pages.
setAttestationChallengeAPI with the challenge received from the server and should then retrieve the attestation certificate chain using the
StrongBox. The client supports hardware-level key attestation if security level is
StrongBoxand attestation certificate chain contains a root certificate signed with Google attestation root key.
Note, if for any reason that process fails, it means that the key is not in security hardware. That does not mean that the key is compromised.
fmt: Attestation statement format identifier
authData: It denotes the authenticator data for the attestation
alg: The algorithm that is used for the Signature
x5c: Attestation certificate chain
sigis generated by concatenating
clientDataHash(challenge sent by the server) and signing through the credential private key using the
algsigning algorithm and the same is verified at the server-side by using the public key in the first certificate.
AndroidKeystoregenerates a key pair using
PURPOSE_WRAP_KEYwhich should also be protected with an attestation certificate, this pair aims to protect the Keys being imported to
AndroidKeystore. The encrypted keys are generated as ASN.1-encoded message in the
SecureKeyWrapperformat which also contains a description of the ways the imported key is allowed to be used. The keys are then decrypted inside the
AndroidKeystorehardware belonging to the specific device that generated the wrapping key so they never appear as plaintext in the device's host memory.
algorithmparameter Specifies the cryptographic algorithm with which the key is used
keySizeparameter Specifies the size, in bits, of the key, measuring in the normal way for the key's algorithm
digestparameter Specifies the digest algorithms that may be used with the key to perform signing and verification operations
KeyStore.getInstance("BKS", "BC") method, where "BKS" is the KeyStore name (BouncyCastle Keystore) and "BC" is the provider (BouncyCastle). You can also use SpongyCastle as a wrapper and initialize the KeyStore as follows:
KeyChainclass. Complete the following steps:
import java.security.SecureRandom, and corresponding usages.
store(OutputStream stream, char password)function to store the KeyStore to disk with a password. Make sure that the password is provided by the user, not hard-coded.
Please note that if the keys are stored on the server, the app need to be online to decrypt the data. This might be a limitation in some use case of mobile apps and should be carefully thought through as this becomes part of the architecture of the app.
AndroidKeyStoreAPI has been changed significantly throughout various versions of Android. In earlier versions, the
AndroidKeyStoreAPI only supported storing public/private key pairs (e.g., RSA). Symmetric key support has only been added since Android 6.0 (API level 23). As a result, a developer needs to handle the different Android API levels to securely store symmetric keys.
AndroidKeyStore. The encrypted symmetric key can encoded using base64 and stored in the
SharedPreferences. Whenever we need the symmetric key, the application retrieves the private key from the
AndroidKeyStoreand decrypts the symmetric key.
/sdcard/. Obviously, hardcoded encryption keys are not the way to go. This means every instance of the application uses the same encryption key. An attacker needs only to do the work once, to extract the key from the source code - whether stored natively or in Java/Kotlin. Consequently, an attacker can decrypt any other data which was encrypted by the application. Next, when you have a predictable key derivation function based on identifiers which are accessible to other applications, the attacker only needs to find the KDF and apply it to the device in order to find the key. Lastly, storing encryption keys publicly also is highly discouraged as other applications can have permission to read the public partition and steal the keys.
Please keep in mind that as long as the key is not stored in the KeyStore, it is always possible to easily retrieve the key on a rooted device and then decrypt the values you are trying to protect.
adb backupcommand to create full data backups and backups of an app's data directory.
AndroidManifest.xmlfor read/write external storage permissions, for example,
MODE_WORLD_WRITABLE: You should avoid using
MODE_WORLD_READABLEfor files because any app will be able to read from or write to the files, even if they are stored in the app's private data directory. If data must be shared with other applications, consider a content provider. A content provider offers read and write permissions to other apps and can grant dynamic permission on a case-by-case basis.
SharedPreferencesclass ( stores key-value pairs)
FileOutPutStreamclass (uses internal or external storage)
getExternal*functions (use external storage)
getWritableDatabasefunction (returns a SQLiteDatabase for writing)
getReadableDatabasefunction (returns a SQLiteDatabase for reading)
getExternalCacheDirsfunction (use cached files)
/data/data/<package-name>/shared_prefs) for sensitive information. Shared Preferences are insecure and unencrypted by default. Some apps might opt to use secure-preferences to encrypt the values stored in Shared Preferences.
/data/data/<package-name>. Only the user and group created when you installed the app (e.g., u0_a82) should have user read, write, and execute permissions (
rwx). Other users should not have permission to access files, but they may have execute permissions for directories.
/data/data/<package-name>/files/, whether it is unencrypted, and whether it contains sensitive information. By default, the file extension is
realmand the file name is
default. Inspect the Realm database with the Realm Browser.
Note: Similar holds for private accessible data on a rooted device
SharedPreferences.Editorto read or write int/boolean/long values, you cannot check whether the data is overridden or not. However: it can hardly be used for actual attacks other than chaining the values (e.g. no additional exploits can be packed which will take over the control flow). In the case of a
StringSetyou should be careful with how the data is interpreted. Using reflection based persistence? Check the section on "Testing Object Persistence" for Android to see how it should be validated. Using the
SharedPreferences.Editorto store and read certificates or keys? Make sure you have patched your security provider given vulnerabilities such as found in Bouncy Castle.
SharedPreferences.Editor) are used, the data needs to be validated the moment it is read from the storage mechanism.
android.util.Logclass have been removed, check the ProGuard configuration file (proguard-rules.pro) for the following options (according to this example of removing logging code and this article about enabling ProGuard in an Android Studio project):
StringBuilderto construct the log statement:
/data/data/<package-name>). Check the application logs to determine whether log data has been generated; some mobile applications create and store their own logs in the data directory.
printStackTraceinstead of a proper logging class. Therefore, your testing strategy must include all output generated while the application is starting, running and closing. To determine what data is directly printed by
printStackTrace, you can use
Logcatas explained in the chapter "Basic Security Testing", section "Monitoring System Logs".
If you already know the app PID you may give it directly using
logcat's regex flags
-e <expr>, --regex=<expr>for example) if you expect certain strings or patterns to come up in the logs.
NotificationListenerServiceto listen for notifications on the device and then send them to attacker-controlled C2 infrastructure. Commonly this is done in order to listen for two-factor authentication (2FA) codes that appear as notifications on the device which are then sent to the attacker. A safer alternative for the user would be to use a 2FA application that does not generate notifications.
NotificationManagerclass which might be an indication of some form of notification management. If the class is being used, the next step would be to understand how the application is generating the notifications and which data ends up being shown.
NotificationCompat.Builder. Observe the trace in the end and evaluate if it contains any sensitive information which another app might have eavesdropped.
TextViewsthat have XML attributes. If the XML attribute
android:inputTypeis given the value
textNoSuggestions, the keyboard cache will not be shown when the input field is selected. The user will have to type everything manually.
AndroidManifest.xmlto detect content providers exposed by the app. You can identify content providers by the
<provider>element. Complete the following steps:
"true". Even if it is not, the tag will be set to
"true"automatically if an
<intent-filter>has been defined for the tag. If the content is meant to be accessed only by the app itself, set
"false". If not, set the flag to
"true"and define proper read/write permissions.
android:permission). Permission tags limit exposure to other apps.
android:protectionLevelattribute has the value
signature. This setting indicates that the data is intended to be accessed only by apps from the same enterprise (i.e., signed with the same key). To make the data accessible to other apps, apply a security policy with the
<permission>element and set a proper
android:protectionLevel. If you use
android:permission, other applications must declare corresponding
<uses-permission>elements in their manifests to interact with your content provider. You can use the
android:grantUriPermissionsattribute to grant more specific access to other apps; you can limit access with the
To avoid SQL injection attacks within the app, use parameterized query methods, such as
delete. Be sure to properly sanitize all method arguments; for example, the
selectionargument could lead to SQL injection if it is made up of concatenated user input.
delete) are being used to prevent SQL injection. If so, make sure all their arguments are properly sanitized.
AndroidManifest.xmlabove, the application exports two content providers. Note that one path ("/Keys") is protected by read and write permissions.
queryfunction in the
DBContentProvider.javafile to determine whether any sensitive information is being leaked:
Passwords/. We will address this in the "Dynamic Analysis" section and show the exact URI that is required.
/Keyspath in the
DBContentProvider. With this information, you can reconstruct part of the content URIs to access the
DBContentProvider(the URIs begin with
scanner.provider.findurismodule. This module guesses paths and determines accessible content URIs in several ways:
app.provider.queryto test for SQL injection by manipulating the projection and selection fields that are passed to the content provider:
app.provider.queryis used to list all the database tables: