Awesome-omni-skill mobile-security
Android security patterns for secure storage, network security, input validation, and authentication.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/testing-security/mobile-security" ~/.claude/skills/diegosouzapw-awesome-omni-skill-mobile-security && rm -rf "$T"
manifest:
skills/testing-security/mobile-security/SKILL.mdsource content
Mobile Security Patterns
Security best practices for Android.
Secure Storage
EncryptedSharedPreferences
// Create encrypted preferences private fun createSecurePrefs(context: Context): SharedPreferences { val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() return EncryptedSharedPreferences.create( context, "secure_prefs", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) } // Usage class TokenStorage(context: Context) { private val prefs = createSecurePrefs(context) var accessToken: String? get() = prefs.getString("access_token", null) set(value) = prefs.edit().putString("access_token", value).apply() fun clear() = prefs.edit().clear().apply() }
Android Keystore
// Generate key in Keystore fun generateSecretKey(alias: String) { val keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore" ) keyGenerator.init( KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(true) .setUserAuthenticationParameters(300, KeyProperties.AUTH_BIOMETRIC_STRONG) .build() ) keyGenerator.generateKey() }
Network Security
Network Security Config
<!-- res/xml/network_security_config.xml --> <?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="false"> <trust-anchors> <certificates src="system"/> </trust-anchors> </base-config> <!-- Debug only --> <debug-overrides> <trust-anchors> <certificates src="user"/> </trust-anchors> </debug-overrides> <!-- Certificate pinning --> <domain-config> <domain includeSubdomains="true">api.example.com</domain> <pin-set expiration="2025-12-31"> <pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin> <pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</pin> </pin-set> </domain-config> </network-security-config>
Certificate Pinning (Ktor)
val client = HttpClient(OkHttp) { engine { config { certificatePinner( CertificatePinner.Builder() .add("api.example.com", "sha256/AAAA...") .add("api.example.com", "sha256/BBBB...") // Backup .build() ) } } }
Safe Logging
// ❌ NEVER log sensitive data Log.d("Auth", "Token: $token") // ✅ Release-safe logging with Timber class ReleaseTree : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (priority >= Log.WARN) { // Send to crash reporting Crashlytics.log(priority, tag, message) } } } // In Application if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } else { Timber.plant(ReleaseTree()) }
Input Validation
// Validate before use fun validateEmail(email: String): Result<String> { return when { email.isBlank() -> Result.failure(ValidationError.Empty) !Patterns.EMAIL_ADDRESS.matcher(email).matches() -> Result.failure(ValidationError.InvalidFormat) email.length > 254 -> Result.failure(ValidationError.TooLong) else -> Result.success(email) } } // SQL injection prevention - use parameterized queries @Query("SELECT * FROM users WHERE id = :userId") suspend fun getUser(userId: String): User?
Biometric Authentication
val biometricPrompt = BiometricPrompt( activity, executor, object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { val cipher = result.cryptoObject?.cipher // Use cipher to decrypt sensitive data } } ) val promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("Authenticate") .setNegativeButtonText("Cancel") .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG) .build() biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
ProGuard/R8 Security
# R8 rules for security -keepattributes SourceFile,LineNumberTable # For crash reports only # Obfuscate sensitive classes -repackageclasses 'a' -allowaccessmodification # Remove logging -assumenosideeffects class android.util.Log { public static *** d(...); public static *** v(...); public static *** i(...); }
Remember: Security is not optional. Build it in from the start.