技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
mobile-security【数据与研究】Use this skill when the user asks about mobile security, secure storage, certificate pinning, SSL pinning, biometric authentication, data encryption, ProGuard, obfuscation, or OWASP Mobile Top 10.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Implement mobile security controls covering data-at-rest encryption, network security (certificate pinning), authentication, and code protection against the OWASP Mobile Top 10.
User request includes: mobile security, secure storage, certificate pinning, ssl pinning, mobile auth, biometric, encrypt mobile, proguard, obfuscate, root detection, jailbreak detection, owasp mobile.
A markdown document containing:
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
4096 tokens
┌─────────────────────────────────────────────────┐
│ Code Protection Layer │
│ ProGuard/R8 obfuscation, debug detection, │
│ root/jailbreak detection, integrity checks │
├─────────────────────────────────────────────────┤
│ Authentication Layer │
│ Biometric (Face ID / fingerprint), token │
│ storage, OAuth2 PKCE, session management │
├─────────────────────────────────────────────────┤
│ Network Security Layer │
│ Certificate pinning, TLS 1.2+, ATS, no │
│ cleartext traffic, anti-phishing │
├─────────────────────────────────────────────────┤
│ Data at Rest Layer │
│ Keychain (iOS), EncryptedSharedPrefs (Android), │
│ flutter_secure_storage, SQLCipher, backup │
│ exclusion │
├─────────────────────────────────────────────────┤
│ Threat Modeling & Compliance │
│ Data classification, OWASP review, privacy │
│ controls, penetration testing schedule │
└─────────────────────────────────────────────────┘
What data does the app handle?
├── PII (name, email, phone, address)
│ ├── Encrypted storage at rest
│ ├── Certificate pinning for all API calls
│ ├── Biometric gate for display
│ ├── Data deletion API required
│ └── Privacy policy labels required
├── Payment data (credit card, billing)
│ ├── PCI DSS scope assessment needed
│ ├── Tokenization — never store raw PAN
│ ├── Certificate pinning mandatory
│ ├── Biometric confirmation for payments
│ └── Network security config hardened
├── Health data (HIPAA)
│ ├── All of the above +
│ ├── Audit logging for all data access
│ ├── BAA agreement with cloud providers
│ ├── Encryption at rest + in transit (always)
│ └── Penetration testing required before launch
└── Non-sensitive (public data, no auth)
├── HTTPS only (TLS 1.2+)
├── Basic storage security
└── No biometric or pinning needed
Identify data classification levels, threat vectors (reverse engineering, network interception, device theft), and compliance requirements.
Use platform-native secure storage: iOS Keychain, Android EncryptedSharedPreferences, flutter_secure_storage, or react-native-keychain.
Implement certificate pinning with backup pins, disable cleartext traffic, and validate TLS connections.
Integrate biometric authentication (Face ID, fingerprint) for sensitive operations and secure token storage.
Enable ProGuard/R8 minification, debug detection, and root/jailbreak detection for release builds.
Implement runtime application self-protection (RASP) controls: debugger detection (prevent debugging in release builds), emulator detection (block or limit functionality in emulators), integrity verification (validate code signature at runtime), and anti-tampering (verify app hash against known good value). Runtime checks should degrade gracefully — log and limit rather than crash.
Schedule penetration testing at each major release. Cover: network interception testing with Burp Suite/mitmproxy, static analysis with MobSF, dynamic analysis on jailbroken/rooted devices, storage inspection via Objection/Frida, and API security testing for OWASP API Top 10. Document findings, track remediation in the security backlog, and re-test after fixes.
owasp_mobile_top_10:
M1_improper_credential_usage:
risk: "Credentials (keys, passwords, tokens) hardcoded or weakly stored"
mitigation: "Platform secure storage (Keychain, EncryptedSharedPrefs), biometric gate"
M2_inadequate_supply_chain:
risk: "Third-party libraries with vulnerabilities, compromised SDKs"
mitigation: "SCA scanning (Snyk, Dependabot), pin dependency versions, binary integrity checks"
M3_insecure_authentication:
risk: "Weak auth flows, no biometric, token theft through device access"
mitigation: "OAuth2 with PKCE, biometric for sensitive operations, token binding to device"
M4_insufficient_authorization:
risk: "Client-side authorization checks that can be bypassed"
mitigation: "Server-enforced authorization — never trust client claims alone"
M5_inadequate_data_privacy:
risk: "PII leakage through logging, analytics, or clipboard"
mitigation: "Data minimization, opt-in analytics, disable clipboard for sensitive fields"
M6_insecure_data_storage:
risk: "Sensitive data in SharedPreferences, UserDefaults, SQLite without encryption"
mitigation: "Encrypted storage for all sensitive data, exclude from backups"
M7_insecure_communication:
risk: "Cleartext HTTP, weak TLS, no certificate validation, no pinning"
mitigation: "HTTPS only, TLS 1.2+, certificate pinning, ATS enforcement (iOS)"
M8_inadequate_privacy_controls:
risk: "Permissions requested but not scoped, privacy labels not accurate"
mitigation: "Minimal permission requests, purpose strings, on-demand permission prompts"
M9_insecure_data_export:
risk: "Clipboard access, app background snapshot, file provider exposure"
mitigation: "Disable clipboard for sensitive data, secure window flag, scoped file access"
M10_misconfiguration:
risk: "Debug mode in release, backup enabled, insecure intent filters"
mitigation: "Build config validation, proguard rules review, intent filter hardening"
security_decision_tree:
question_1_data_sensitivity:
"Does the app handle PII, payment data, or credentials?":
yes: "Full security controls required — all layers below"
no: "Baseline controls only — HTTPS, basic storage security"
question_2_auth_method:
"What authentication mechanism does the app use?":
token_based: "Secure token storage + biometric gate for display"
session_cookie: "HTTP-only cookies + CSRF protection"
biometric_only: "Device biometric + fallback to PIN if biometric unavailable"
question_3_network_security:
"Does the app communicate with a custom API?":
yes: "Certificate pinning required — at least 2 backup pins"
no_first_party: "Standard TLS validation sufficient"
third_party_only: "Validate third-party SDK security before integration"
question_4_code_protection:
"What is the risk of reverse engineering?":
high: "ProGuard/R8 full obfuscation, root/jailbreak detection, integrity checks"
medium: "ProGuard/R8 default configuration, basic obfuscation"
low: "Minify only, no obfuscation needed"
// iOS: Keychain
let query: [String: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: "auth_token",
kSecValueData: token.data(using: .utf8)!,
]
SecItemAdd(query as CFDictionary, nil)
// Android: EncryptedSharedPreferences
val prefs = EncryptedSharedPreferences.create(
"secure_prefs",
masterKey,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// Flutter: flutter_secure_storage
final storage = FlutterSecureStorage();
await storage.write(key: 'token', value: token);
// RN: react-native-keychain
await Keychain.setGenericPassword('token', token);
<!-- Android: network_security_config.xml -->
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2025-12-31">
<pin digest="SHA-256">base64hash1...</pin>
<pin digest="SHA-256">base64hash2...</pin>
<pin digest="SHA-256">base64hash3...</pin> <!-- Backup pin -->
</pin-set>
<!-- Trust user CA for debugging only — remove in release -->
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</domain-config>
</network-security-config>
<!-- AndroidManifest.xml — reference config -->
<application
android:networkSecurityConfig="@xml/network_security_config"
android:allowBackup="false"
android:fullBackupContent="false">
// iOS: URLSessionDelegate — certificate pinning with backup pins
class PinningDelegate: NSObject, URLSessionDelegate {
private let pinnedHashes = [
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Primary
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Backup 1
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=", // Backup 2
]
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
// Get public key hash
let publicKey = SecCertificateCopyKey(certificate)
let publicKeyData = SecKeyCopyExternalRepresentation(publicKey!, nil)! as Data
let hash = sha256(data: publicKeyData)
if pinnedHashes.contains(hash) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil) // MITM detected
}
}
private func sha256(data: Data) -> String {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes { CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) }
return Data(hash).base64EncodedString()
}
}
// Android: OkHttp CertificatePinner
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAA...")
.add("api.example.com", "sha256/BBBB...")
.add("api.example.com", "sha256/CCCC...") // Backup
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
<!-- Info.plist — App Transport Security -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>api.example.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<true/>
</dict>
</dict>
</dict>
// Flutter — local_auth
import 'package:local_auth/local_auth.dart';
final auth = LocalAuthentication();
bool authenticated = false;
Future<bool> authenticateWithBiometrics() async {
final available = await auth.canCheckBiometrics;
if (!available) return false; // Fall back to PIN
try {
authenticated = await auth.authenticate(
localizedReason: 'Authenticate to view your orders',
options: AuthenticationOptions(
biometricOnly: false, // Allow PIN/password fallback
stickyAuth: true, // Re-authenticate on resume
),
);
} on PlatformException catch (e) {
if (e.code == 'LockedOut' || e.code == 'PermanentlyLockedOut') {
// Biometric locked out — redirect to device settings
openAppSettings();
}
}
return authenticated;
}
// iOS — LocalAuthentication
import LocalAuthentication
class BiometricService {
let context = LAContext()
func authenticate(reason: String) async throws -> Bool {
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
throw BiometricError.notAvailable
}
return try await context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: reason
)
}
var biometryType: LABiometryType {
context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
return context.biometryType // .faceID, .touchID, or .none
}
}
// Android — BiometricPrompt
class BiometricHelper(private val activity: FragmentActivity) {
private val executor = ContextCompat.getMainExecutor(activity)
fun authenticate(onSuccess: () -> Unit, onError: (String) -> Unit) {
val biometricManager = BiometricManager.from(activity)
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
BiometricManager.BIOMETRIC_SUCCESS -> {
val prompt = BiometricPrompt(activity, executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
onSuccess()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
onError(errString.toString())
}
})
prompt.authenticate(
BiometricPrompt.PromptInfo.Builder()
.setTitle("Authenticate")
.setSubtitle("Unlock to view orders")
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or
BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.build()
)
}
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
onError("Biometric not available")
}
}
}
}
// React Native — OAuth2 with PKCE
import { authorize } from 'react-native-app-auth';
const config = {
issuer: 'https://accounts.example.com',
clientId: 'mobile_client',
redirectUrl: 'com.example.app://oauth/callback',
scopes: ['openid', 'profile', 'orders'],
useNonce: true,
usePKCE: true, // PKCE prevents auth code interception
};
async function login() {
try {
const result = await authorize(config);
// Store tokens in secure storage
await Keychain.setGenericPassword('access_token', result.accessToken);
await Keychain.setGenericPassword('refresh_token', result.refreshToken);
return result;
} catch (error) {
if ((error as AuthError).code === 'cancelled') {
return null; // User cancelled
}
throw error;
}
}
// iOS — Keychain wrapper
class KeychainManager {
static let shared = KeychainManager()
func save(key: String, value: String) {
let data = value.data(using: .utf8)!
let query: [String: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecValueData: data,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
]
SecItemDelete(query as CFDictionary) // Delete existing
SecItemAdd(query as CFDictionary, nil)
}
func read(key: String) -> String? {
let query: [String: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
kSecReturnData: true,
]
var result: AnyObject?
SecItemCopyMatching(query as CFDictionary, &result)
guard let data = result as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
func delete(key: String) {
let query: [String: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: key,
]
SecItemDelete(query as CFDictionary)
}
}
// Android — Encrypted database with SQLCipher
import net.zetetic.database.sqlcipher.SQLiteDatabase
import net.zetetic.database.sqlcipher.SupportFactory
val passphrase = SQLiteDatabase.getBytes(masterKey.toCharArray())
val factory = SupportFactory(passphrase)
val db = Room.databaseBuilder(context, AppDatabase::class.java, "encrypted.db")
.openHelperFactory(factory)
.build()
// iOS — Core Data with NSFileProtection
let store = NSPersistentStoreCoordinator()
let options = [
NSPersistentHistoryTrackingKey: true,
NSPersistentStoreFileProtectionKey: FileProtectionType.complete,
]
try store.addPersistentStore(
ofType: NSSQLiteStoreType,
configurationName: nil,
at: storeURL,
options: options
)
// app/build.gradle.kts
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
# proguard-rules.pro — keep what's needed
-keep class com.example.app.model.** { *; } # Keep data models (serialization)
-keep class com.example.app.api.** { *; } # Keep API interfaces (Retrofit)
-keepattributes Signature, *Annotation*
-dontwarn okhttp3.**
# Flutter: build.gradle android settings
--obfuscate
--split-debug-info=build/debug-info
// iOS — debugger detection
func isDebuggerAttached() -> Bool {
var info = kinfo_proc()
var size = MemoryLayout<kinfo_proc>.stride
var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
let result = sysctl(&mib, u_int(mib.count), &info, &size, nil, 0)
return result == 0 && (info.kp_proc.p_flag & P_TRACED) != 0
}
// Android — Play Integrity API
class IntegrityVerifier(private val context: Context) {
private val integrityManager = IntegrityManagerFactory.create(context)
suspend fun verifyIntegrity(): IntegrityResult {
val tokenResponse = integrityManager.requestIntegrityToken(
IntegrityTokenRequest.AccessTokenRequest(
cloudProjectNumber = CLOUD_PROJECT_NUMBER,
nonce = generateNonce()
)
)
val integrityToken = tokenResponse.token()
// Send token to server for verification
return api.verifyIntegrityToken(integrityToken, nonce)
}
}
// iOS — DeviceCheck
import DeviceCheck
func verifyDeviceIntegrity() async -> Bool {
let device = DCDevice.current
guard device.isSupported else { return false }
do {
let token = try await device.generateToken()
// Send token to server for verification
return try await api.verifyDeviceToken(token)
} catch {
return false
}
}
// iOS — disable pasteboard for sensitive fields
// SecureField automatically disables copy/paste in iOS
// Prevent screenshots for sensitive screens
// In the view controller:
view.clipsToBounds = true
// Using SecureField for text input disables clipboard access
// UIPasteboard notification
UIPasteboard.general.changeCount // Monitor for clipboard changes
// Android — disable clipboard for sensitive TextFields
// Disable copy/paste on EditText
editText.customSelectionActionModeCallback = object : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean = true
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean = false
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean = false
override fun onDestroyActionMode(mode: ActionMode) {}
}
// Prevent screenshots
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
// Clear clipboard after sensitive copy
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.clearPrimaryClip()
<!-- Android: disable backup entirely for sensitive apps -->
<application ...
android:allowBackup="false"
android:fullBackupContent="false"
android:dataExtractionRules="@xml/data_extraction_rules">
<!-- Or exclude only sensitive files -->
<full-backup-content>
<exclude domain="sharedpref" path="secure_prefs.xml"/>
<exclude domain="database" path="encrypted.db"/>
</full-backup-content>
// iOS — exclude files from backup
var url = documentsURL.appendingPathComponent("database.sqlite")
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
try url.setResourceValues(resourceValues)
NSURLIsExcludedFromBackupKey (iOS) or android:allowBackup="false".staging, dev, or debug API endpoints left in release builds allow attackers to access non-production environments. Strip debug configurations at build time.| Security Approach | Effort | Protection Level | User Friction |
|---|---|---|---|
| Platform secure storage only | Low | Medium (basic data protection) | None |
| + Certificate pinning | Medium | High (prevents MITM) | None |
| + Biometric auth | Medium | High (device-level auth) | Low |
| + ProGuard/R8 obfuscation | Low | Medium (deters static reverse engineering) | None |
| + Root/jailbreak detection | Medium | Medium-High (prevents tampered-device attacks) | None |
| + Runtime protection (RASP) | High | High (active attack prevention) | None |
| + Full encryption (SQLCipher) | High | Very High (data never in plaintext) | None |
| + Penetration testing + reward | Ongoing | Highest (validated through attack simulation) | None |
| Tool | Category | Platform |
|---|---|---|
| MobSF | Static + dynamic analysis | iOS, Android |
| Burp Suite | Network interception testing | iOS, Android |
| OWASP ZAP | Automated security scanning | iOS, Android |
| Frida | Runtime instrumentation | iOS, Android |
| Objection | Mobile exploration | iOS, Android |
| Drozer | Android security assessment | Android |
| Needle | iOS security testing | iOS |
| Snyk / Dependabot | SCA (supply chain) | Cross-platform |
| Checkmarx / SonarQube | SAST (static analysis) | Cross-platform |
| Apple Security Bounty | Bug bounty platform | iOS |
| Google Play Security Rewards | Bug bounty platform | Android |
| Selenium / Appium | Security UI automation | iOS, Android |
Hand off to stack-specific skill for implementation.