Files
2nd/10_Wiki/Topics/Coding/Security_Mobile_Hardening.md
T
2026-05-10 22:08:15 +09:00

363 lines
8.8 KiB
Markdown

---
id: security-mobile-hardening
title: Mobile Security — root / jailbreak / SSL pin
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [mobile, security, vibe-coding]
tech_stack: { language: "Swift / Kotlin", applicable_to: ["iOS", "Android"] }
applied_in: []
aliases: [mobile security, root detection, jailbreak detection, SSL pinning, ProGuard, code obfuscation, anti-tamper]
---
# Mobile Security Hardening
> 사용자 device 가 untrusted. **Root/jailbreak detect, SSL pinning, obfuscation, anti-tamper**. 100% 막을 수 X — cost ↑.
## 📖 핵심 개념
- 매 device 가 attacker (악성 user / malware).
- Defense-in-depth (multiple layer).
- Crypto key 가 client X.
- Server 가 trust boundary.
## 💻 코드 패턴
### Root / Jailbreak detection
```swift
// iOS
func isJailbroken() -> Bool {
let paths = ["/Applications/Cydia.app", "/usr/sbin/sshd", "/etc/apt"]
return paths.contains { FileManager.default.fileExists(atPath: $0) }
|| canOpen(URL(string: "cydia://")!)
}
```
```kotlin
// Android
fun isRooted(): Boolean {
val paths = listOf("/system/app/Superuser.apk", "/system/xbin/su", "/system/bin/su")
if (paths.any { File(it).exists() }) return true
return try { Runtime.getRuntime().exec("which su").waitFor() == 0 } catch (e: Exception) { false }
}
```
→ Detect 만. Block 가 user-friendly X.
### SSL Pinning
```swift
// iOS URLSessionDelegate
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let serverTrust = challenge.protectionSpace.serverTrust,
let cert = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let serverCertData = SecCertificateCopyData(cert) as Data
let pinnedCertData = // ... loaded from bundle
if serverCertData == pinnedCertData {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
```
```kotlin
// Android (OkHttp)
val pinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build()
val client = OkHttpClient.Builder().certificatePinner(pinner).build()
```
→ MITM 방지. Cert rotation 시 update.
### Code obfuscation (Android)
```gradle
// app/build.gradle
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
```
```proguard
# proguard-rules.pro
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
# Keep models for serialization
-keep class com.example.models.** { *; }
```
→ R8 (modern) — 작은 + 빠른.
### iOS obfuscation
```
Xcode 가 native obfuscation X.
- Symbol stripping (release build).
- Swift name mangling 가 자체.
- 외부 tool (Swift Shield, etc.) 가 추가.
```
→ Symbol stripping 가 baseline.
### Anti-debug
```swift
// iOS
func isDebuggerAttached() -> Bool {
var info = kinfo_proc()
var size = MemoryLayout<kinfo_proc>.size
var name: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
let r = sysctl(&name, 4, &info, &size, nil, 0)
return r == 0 && (info.kp_proc.p_flag & P_TRACED) != 0
}
```
```kotlin
// Android
fun isDebuggerAttached(): Boolean = Debug.isDebuggerConnected()
```
### Secret 의 storage
```swift
// iOS Keychain
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: 'token',
kSecValueData as String: token.data(using: .utf8)!,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
]
SecItemAdd(query as CFDictionary, nil)
```
```kotlin
// Android EncryptedSharedPreferences
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val prefs = EncryptedSharedPreferences.create(
context, "secret-prefs", masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
prefs.edit().putString('token', token).apply()
```
→ Hardware-backed (TEE / Secure Enclave).
### Biometric auth
```swift
// iOS
let context = LAContext()
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: 'Login') { success, error in
if success {
// Allow access.
}
}
```
```kotlin
// Android
val biometric = BiometricPrompt(this, executor, callback)
biometric.authenticate(BiometricPrompt.PromptInfo.Builder()
.setTitle('Login')
.setNegativeButtonText('Cancel')
.build())
```
### Token refresh
```ts
// Short-lived access token (15 min).
// Long-lived refresh token (Keychain).
// 401 → refresh → retry.
async function fetchWithRefresh(url: string) {
let r = await fetch(url, { headers: { Authorization: `Bearer ${accessToken}` } });
if (r.status === 401) {
accessToken = await refresh();
r = await fetch(url, { headers: { Authorization: `Bearer ${accessToken}` } });
}
return r;
}
```
### Anti-tamper
```
- App signature check (release build).
- Integrity check (file hash).
- Native code (NDK) 가 더 어려움 to tamper.
```
→ 100% prevent X. Cost ↑.
### Secret in code (안 됨)
```swift
// API key in code
let API_KEY = 'sk_live_xxx'
```
→ Decompile 가 trivial. Server 가 proxy.
### Network security config (Android)
```xml
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config>
<domain includeSubdomains='true'>api.example.com</domain>
<pin-set>
<pin digest='SHA-256'>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
</pin-set>
<trust-anchors>
<certificates src='system' />
</trust-anchors>
</domain-config>
</network-security-config>
```
```xml
<!-- AndroidManifest.xml -->
<application android:networkSecurityConfig='@xml/network_security_config'>
```
### App Transport Security (iOS)
```xml
<!-- Info.plist -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
```
→ HTTPS-only default.
### Reverse engineering tool
```
- Frida (runtime hook).
- Cycript (iOS).
- IDA Pro / Ghidra (decompile).
- Charles Proxy / mitmproxy (network).
- Cydia / jailbroken iOS.
- Magisk (root Android).
→ Defense-in-depth 가 cost ↑.
```
### Frida detection
```swift
// iOS
let dlopen_handle = dlopen('frida-gum', RTLD_NOLOAD)
if dlopen_handle != nil {
// Frida detected.
}
```
→ 또 bypass 가능. Cat-and-mouse.
### 결론
```
모든 client = trustless.
- 매 critical logic = server.
- 매 secret = server.
- Client = UI 만 신뢰.
Hardening:
- Rate limit (server).
- Anomaly detect (server).
- Token rotation.
- Device attestation.
```
### Device attestation
```swift
// iOS App Attest
let attestService = DCAppAttestService.shared
attestService.generateKey { keyId, error in
attestService.attestKey(keyId, clientDataHash: hash) { attestation, error in
// Send to server.
}
}
```
```kotlin
// Android Play Integrity
val integrityManager = IntegrityManagerFactory.create(context)
val task = integrityManager.requestIntegrityToken(
IntegrityTokenRequest.builder().setNonce(nonce).build()
)
```
→ Apple / Google 가 device 의 integrity 검증.
서버 가 token 가 valid 면 trust.
### OWASP Mobile Top 10
```
M1: Improper credential usage
M2: Inadequate supply chain
M3: Insecure auth
M4: Insufficient input validation
M5: Insecure communication
M6: Inadequate privacy
M7: Insufficient binary protection
M8: Security misconfiguration
M9: Insecure data storage
M10: Insufficient cryptography
```
→ Reference.
### Cost 인지
```
Strict hardening:
- Setup cost.
- Maintenance (cert rotation, ProGuard tuning).
- Crash 가능 (false positive).
- 사용자 friction (jailbreak detect).
→ 위험 가 가치 가져야.
- Banking app: 큰 hardening.
- Casual game: 작은.
```
## 🤔 의사결정 기준
| 위험 | 추천 |
|---|---|
| Banking | 모든 (root + pin + obfuscate + attest) |
| E-commerce | SSL pin + token rotation |
| Casual | HTTPS + Keychain |
| Internal | Token + biometric |
| Game | Anti-cheat (server) |
## ❌ 안티패턴
- **Secret in code**: decompile.
- **Plain HTTP**: MITM.
- **Custom crypto**: bug.
- **Local storage 가 plain**: extract.
- **No SSL pin (sensitive)**: MITM.
- **Trust client**: server 가 trust boundary.
## 🤖 LLM 활용 힌트
- Defense-in-depth (multiple layer).
- Server 가 trust boundary.
- App attestation 가 modern.
- Hardening cost vs 위험 trade-off.
## 🔗 관련 문서
- [[Security_Auth_Authz_Patterns]]
- [[Security_Secrets_Management]]
- [[iOS_Keychain_Storage]]