From everything-claude-code-mobile
Provides Android security patterns for secure storage with EncryptedSharedPreferences and Keystore, network security configs, certificate pinning, input validation, and authentication.
npx claudepluginhub ahmed3elshaer/everything-claude-code-mobile --plugin everything-claude-code-mobileThis skill uses the workspace's default tool permissions.
Security best practices for Android.
Implements secure mobile coding practices for input validation, WebView security, secure data storage like Keychain/Keystore, and mobile authentication patterns.
Implements and reviews secure mobile coding practices including input validation, WebView security, data storage, and authentication patterns for iOS/Android apps.
Reviews static security in Flutter/Dart mobile apps: hardcoded secrets, secure storage, HTTPS, crypto libs, auth enforcement, logging, deps scans, backups. Use for secrets, data, network, auth, crypto code.
Share bugs, ideas, or general feedback.
Security best practices for Android.
// 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()
}
// 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()
}
<!-- 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>
val client = HttpClient(OkHttp) {
engine {
config {
certificatePinner(
CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAA...")
.add("api.example.com", "sha256/BBBB...") // Backup
.build()
)
}
}
}
// ❌ 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())
}
// 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?
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))
# 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.