第十四章:防护设计最佳实践

8 阅读12分钟

第十四章:防护设计最佳实践

本章字数:约17000字 阅读时间:约60分钟 难度等级:★★★★☆

声明:本文中的公司名称、包名、API地址、密钥等均已脱敏处理。文中的"梦想世界"、"dreamworld"等均为虚构名称,与任何真实公司无关。


引言

经过前面章节的攻防分析,我们已经深入了解了移动应用面临的安全威胁和各种攻击手段。本章将从防御者的角度,系统性地介绍如何设计和实现安全的移动应用。


14.1 安全架构设计原则

14.1.1 纵深防御

┌─────────────────────────────────────────────────────────────────┐
│                    纵深防御架构                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    第一层:网络层                        │    │
│  │  • HTTPS强制                                             │    │
│  │  • 证书固定                                              │    │
│  │  • 代理检测                                              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                            │                                     │
│                            ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    第二层:应用层                        │    │
│  │  • 代码混淆                                              │    │
│  │  • 完整性校验                                            │    │
│  │  • 环境检测                                              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                            │                                     │
│                            ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    第三层:业务层                        │    │
│  │  • 请求签名                                              │    │
│  │  • 参数加密                                              │    │
│  │  • 防重放                                                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                            │                                     │
│                            ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    第四层:数据层                        │    │
│  │  • 本地数据加密                                          │    │
│  │  • 密钥安全存储                                          │    │
│  │  • 敏感数据保护                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                            │                                     │
│                            ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                    第五层:Native层                      │    │
│  │  • 关键逻辑Native化                                      │    │
│  │  • 反调试保护                                            │    │
│  │  • 代码加固                                              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                  │
│  设计原则:                                                      │
│  • 每层独立防护,不依赖其他层                                    │
│  • 单层被突破不会导致全面失守                                    │
│  • 攻击者需要突破所有层才能达成目标                              │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

14.1.2 最小权限原则

/**
 * 最小权限设计示例
 */
public class MinimalPrivilegeDesign {
    
    /**
     * 权限申请最佳实践
     */
    public void requestPermissions() {
        // 1. 只申请必要的权限
        // 不要申请"以防万一"的权限
        
        // 2. 延迟申请
        // 在真正需要时才申请,而不是启动时全部申请
        
        // 3. 解释原因
        // 向用户说明为什么需要这个权限
        
        // 4. 优雅降级
        // 用户拒绝权限时,提供替代方案
    }
    
    /**
     * 数据访问最小化
     */
    public void minimalDataAccess() {
        // 1. 只收集必要的数据
        // 2. 数据本地处理优先
        // 3. 及时清理不需要的数据
        // 4. 敏感数据脱敏处理
    }
    
    /**
     * 组件暴露最小化
     */
    public void minimalComponentExposure() {
        // 1. Activity/Service/Receiver默认不导出
        // android:exported="false"
        
        // 2. ContentProvider设置权限
        // android:readPermission="..."
        // android:writePermission="..."
        
        // 3. Intent过滤器谨慎使用
        // 避免隐式Intent被恶意应用利用
    }
}

14.1.3 安全默认值

<!-- AndroidManifest.xml 安全配置 -->
<application
    android:allowBackup="false"
    android:usesCleartextTraffic="false"
    android:networkSecurityConfig="@xml/network_security_config"
    android:debuggable="false">
    
    <!-- 组件默认不导出 -->
    <activity
        android:name=".MainActivity"
        android:exported="true">
        <!-- 只有主Activity需要导出 -->
    </activity>
    
    <activity
        android:name=".InternalActivity"
        android:exported="false" />
    
    <service
        android:name=".BackgroundService"
        android:exported="false" />
    
    <receiver
        android:name=".InternalReceiver"
        android:exported="false" />
    
    <provider
        android:name=".DataProvider"
        android:exported="false"
        android:grantUriPermissions="false" />
</application>

14.2 网络安全设计

14.2.1 传输层安全

<!-- 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>
    
    <!-- 主域名配置 -->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.dreamworld.com</domain>
        
        <!-- 证书固定 -->
        <pin-set expiration="2027-01-01">
            <!-- 主证书 -->
            <pin digest="SHA-256">base64EncodedSHA256Hash1=</pin>
            <!-- 备用证书 -->
            <pin digest="SHA-256">base64EncodedSHA256Hash2=</pin>
        </pin-set>
    </domain-config>
    
    <!-- 调试配置(仅debug版本) -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>

14.2.2 请求签名设计

/**
 * API请求签名设计
 */
public class ApiSignatureDesign {
    
    /**
     * 签名方案设计
     */
    public static class SignatureScheme {
        
        // 签名要素
        // 1. 时间戳 - 防止重放
        // 2. 随机数 - 增加唯一性
        // 3. 请求参数 - 防止篡改
        // 4. 设备标识 - 绑定设备
        // 5. 密钥 - 验证身份
        
        /**
         * 构建签名字符串
         */
        public String buildSignatureString(Request request) {
            StringBuilder sb = new StringBuilder();
            
            // 按固定顺序拼接
            sb.append(request.getMethod()).append("\n");
            sb.append(request.getPath()).append("\n");
            sb.append(request.getTimestamp()).append("\n");
            sb.append(request.getNonce()).append("\n");
            sb.append(request.getDeviceId()).append("\n");
            
            // 参数排序后拼接
            Map<String, String> params = new TreeMap<>(request.getParams());
            for (Map.Entry<String, String> entry : params.entrySet()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            
            // Body的MD5
            if (request.getBody() != null) {
                sb.append(md5(request.getBody()));
            }
            
            return sb.toString();
        }
        
        /**
         * 计算签名
         */
        public String sign(String data, String key) {
            // 使用HMAC-SHA256
            return hmacSha256(data, key);
        }
    }
    
    /**
     * 防重放设计
     */
    public static class AntiReplay {
        
        // 服务端检查
        // 1. 时间戳有效期(如5分钟内)
        // 2. Nonce唯一性(Redis存储已使用的Nonce)
        // 3. 签名有效性
        
        /**
         * 验证请求
         */
        public boolean validateRequest(Request request) {
            // 1. 检查时间戳
            long now = System.currentTimeMillis();
            long timestamp = request.getTimestamp();
            if (Math.abs(now - timestamp) > 5 * 60 * 1000) {
                return false;  // 时间戳过期
            }
            
            // 2. 检查Nonce
            String nonce = request.getNonce();
            if (isNonceUsed(nonce)) {
                return false;  // Nonce已使用
            }
            markNonceUsed(nonce);
            
            // 3. 验证签名
            String expectedSign = calculateSign(request);
            if (!expectedSign.equals(request.getSign())) {
                return false;  // 签名无效
            }
            
            return true;
        }
    }
}

14.2.3 数据加密传输

/**
 * 敏感数据加密传输
 */
public class DataEncryption {
    
    /**
     * 请求加密
     */
    public String encryptRequest(String plainText, String aesKey) {
        // 1. 生成随机IV
        byte[] iv = generateRandomIV();
        
        // 2. AES-GCM加密
        byte[] encrypted = aesGcmEncrypt(plainText.getBytes(), aesKey, iv);
        
        // 3. 组合IV和密文
        byte[] result = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, result, 0, iv.length);
        System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
        
        // 4. Base64编码
        return Base64.encodeToString(result, Base64.NO_WRAP);
    }
    
    /**
     * 响应解密
     */
    public String decryptResponse(String cipherText, String aesKey) {
        // 1. Base64解码
        byte[] data = Base64.decode(cipherText, Base64.NO_WRAP);
        
        // 2. 分离IV和密文
        byte[] iv = Arrays.copyOfRange(data, 0, 12);
        byte[] encrypted = Arrays.copyOfRange(data, 12, data.length);
        
        // 3. AES-GCM解密
        byte[] decrypted = aesGcmDecrypt(encrypted, aesKey, iv);
        
        return new String(decrypted);
    }
    
    /**
     * 密钥协商
     */
    public byte[] keyExchange(PublicKey serverPublicKey) {
        // 使用ECDH进行密钥协商
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
        keyGen.initialize(new ECGenParameterSpec("secp256r1"));
        KeyPair clientKeyPair = keyGen.generateKeyPair();
        
        // 计算共享密钥
        KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
        keyAgreement.init(clientKeyPair.getPrivate());
        keyAgreement.doPhase(serverPublicKey, true);
        
        byte[] sharedSecret = keyAgreement.generateSecret();
        
        // 派生AES密钥
        return deriveKey(sharedSecret, "AES", 256);
    }
}

14.3 代码保护策略

14.3.1 混淆策略

# proguard-rules.pro - 生产级混淆配置

# 基础优化
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose

# 保留必要的类
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.view.View

# 保留JNI方法
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留Parcelable
-keepclassmembers class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

# 保留序列化
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

# 保留枚举
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 保留注解
-keepattributes *Annotation*
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable

# 混淆字典
-obfuscationdictionary proguard-dictionary.txt
-classobfuscationdictionary proguard-dictionary.txt
-packageobfuscationdictionary proguard-dictionary.txt

# 重新打包到根包
-repackageclasses ''
-allowaccessmodification

# 移除日志
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
    public static *** w(...);
    public static *** e(...);
}

14.3.2 关键代码Native化

/**
 * 关键逻辑Native化设计
 */
public class NativeSecurityModule {
    
    static {
        System.loadLibrary("security");
    }
    
    // 密钥生成 - Native实现
    public static native byte[] generateKey(int length);
    
    // 签名计算 - Native实现
    public static native String calculateSignature(String data, byte[] key);
    
    // 加密操作 - Native实现
    public static native byte[] encrypt(byte[] data, byte[] key);
    
    // 解密操作 - Native实现
    public static native byte[] decrypt(byte[] data, byte[] key);
    
    // 完整性校验 - Native实现
    public static native boolean verifyIntegrity();
    
    // 环境检测 - Native实现
    public static native boolean isSecureEnvironment();
}
// security.c - Native安全模块实现

#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>

// 内部密钥(实际应用中应更安全地存储)
static const unsigned char INTERNAL_KEY[] = {
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

// 生成随机密钥
JNIEXPORT jbyteArray JNICALL
Java_com_dreamworld_NativeSecurityModule_generateKey(
        JNIEnv *env, jclass clazz, jint length) {
    
    unsigned char *key = malloc(length);
    if (RAND_bytes(key, length) != 1) {
        free(key);
        return NULL;
    }
    
    jbyteArray result = (*env)->NewByteArray(env, length);
    (*env)->SetByteArrayRegion(env, result, 0, length, (jbyte*)key);
    
    // 清除内存
    memset(key, 0, length);
    free(key);
    
    return result;
}

// 计算HMAC签名
JNIEXPORT jstring JNICALL
Java_com_dreamworld_NativeSecurityModule_calculateSignature(
        JNIEnv *env, jclass clazz, jstring data, jbyteArray key) {
    
    const char *dataStr = (*env)->GetStringUTFChars(env, data, NULL);
    jbyte *keyBytes = (*env)->GetByteArrayElements(env, key, NULL);
    jsize keyLen = (*env)->GetArrayLength(env, key);
    
    unsigned char result[EVP_MAX_MD_SIZE];
    unsigned int resultLen;
    
    HMAC(EVP_sha256(), keyBytes, keyLen, 
         (unsigned char*)dataStr, strlen(dataStr),
         result, &resultLen);
    
    // 转换为十六进制字符串
    char hexResult[resultLen * 2 + 1];
    for (int i = 0; i < resultLen; i++) {
        sprintf(hexResult + i * 2, "%02x", result[i]);
    }
    
    (*env)->ReleaseStringUTFChars(env, data, dataStr);
    (*env)->ReleaseByteArrayElements(env, key, keyBytes, 0);
    
    return (*env)->NewStringUTF(env, hexResult);
}

// AES加密
JNIEXPORT jbyteArray JNICALL
Java_com_dreamworld_NativeSecurityModule_encrypt(
        JNIEnv *env, jclass clazz, jbyteArray data, jbyteArray key) {
    
    jbyte *dataBytes = (*env)->GetByteArrayElements(env, data, NULL);
    jsize dataLen = (*env)->GetArrayLength(env, data);
    jbyte *keyBytes = (*env)->GetByteArrayElements(env, key, NULL);
    
    // 生成IV
    unsigned char iv[16];
    RAND_bytes(iv, 16);
    
    // 加密
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, 
                       (unsigned char*)keyBytes, iv);
    
    int outLen;
    unsigned char *outBuf = malloc(dataLen + 16 + 16);  // IV + 数据 + Tag
    
    // 复制IV
    memcpy(outBuf, iv, 16);
    
    // 加密数据
    EVP_EncryptUpdate(ctx, outBuf + 16, &outLen, 
                      (unsigned char*)dataBytes, dataLen);
    int totalLen = 16 + outLen;
    
    EVP_EncryptFinal_ex(ctx, outBuf + totalLen, &outLen);
    totalLen += outLen;
    
    // 获取Tag
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, outBuf + totalLen);
    totalLen += 16;
    
    EVP_CIPHER_CTX_free(ctx);
    
    jbyteArray result = (*env)->NewByteArray(env, totalLen);
    (*env)->SetByteArrayRegion(env, result, 0, totalLen, (jbyte*)outBuf);
    
    free(outBuf);
    (*env)->ReleaseByteArrayElements(env, data, dataBytes, 0);
    (*env)->ReleaseByteArrayElements(env, key, keyBytes, 0);
    
    return result;
}

14.4 密钥管理

14.4.1 密钥存储方案

┌─────────────────────────────────────────────────────────────────┐
│                    密钥存储方案对比                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  方案              安全性      易用性      适用场景              │
│  ─────────────────────────────────────────────────────────────  │
│  硬编码            ★☆☆☆☆     ★★★★★     不推荐                │
│                                                                  │
│  SharedPreferences ★★☆☆☆     ★★★★☆     非敏感配置            │
│                                                                  │
│  加密文件          ★★★☆☆     ★★★☆☆     一般敏感数据          │
│                                                                  │
│  Android Keystore  ★★★★☆     ★★★☆☆     密钥存储              │
│                                                                  │
│  TEE/SE            ★★★★★     ★★☆☆☆     高安全场景            │
│                                                                  │
│  服务端存储        ★★★★★     ★★☆☆☆     最敏感数据            │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

14.4.2 Android Keystore使用

/**
 * Android Keystore安全存储
 */
public class SecureKeyStore {
    
    private static final String KEY_ALIAS = "DreamWorldSecureKey";
    private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
    
    /**
     * 生成密钥
     */
    public void generateKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE);
        
        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
            KEY_ALIAS,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .setKeySize(256)
            // 需要用户认证
            .setUserAuthenticationRequired(true)
            .setUserAuthenticationValidityDurationSeconds(30)
            // 绑定到安全硬件
            .setIsStrongBoxBacked(true)
            .build();
        
        keyGenerator.init(spec);
        keyGenerator.generateKey();
    }
    
    /**
     * 加密数据
     */
    public byte[] encrypt(byte[] data) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
        keyStore.load(null);
        
        SecretKey key = (SecretKey) keyStore.getKey(KEY_ALIAS, null);
        
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        
        byte[] iv = cipher.getIV();
        byte[] encrypted = cipher.doFinal(data);
        
        // 组合IV和密文
        byte[] result = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, result, 0, iv.length);
        System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
        
        return result;
    }
    
    /**
     * 解密数据
     */
    public byte[] decrypt(byte[] encryptedData) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
        keyStore.load(null);
        
        SecretKey key = (SecretKey) keyStore.getKey(KEY_ALIAS, null);
        
        // 分离IV和密文
        byte[] iv = Arrays.copyOfRange(encryptedData, 0, 12);
        byte[] encrypted = Arrays.copyOfRange(encryptedData, 12, encryptedData.length);
        
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        
        return cipher.doFinal(encrypted);
    }
    
    /**
     * 检查密钥是否存在
     */
    public boolean keyExists() {
        try {
            KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
            keyStore.load(null);
            return keyStore.containsAlias(KEY_ALIAS);
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 删除密钥
     */
    public void deleteKey() throws Exception {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
        keyStore.load(null);
        keyStore.deleteEntry(KEY_ALIAS);
    }
}

14.4.3 密钥轮换策略

/**
 * 密钥轮换管理
 */
public class KeyRotationManager {
    
    private static final long KEY_VALIDITY_DAYS = 90;  // 密钥有效期90天
    
    /**
     * 检查是否需要轮换密钥
     */
    public boolean needsRotation() {
        long keyCreationTime = getKeyCreationTime();
        long now = System.currentTimeMillis();
        long validityMs = KEY_VALIDITY_DAYS * 24 * 60 * 60 * 1000L;
        
        return (now - keyCreationTime) > validityMs;
    }
    
    /**
     * 执行密钥轮换
     */
    public void rotateKey() {
        // 1. 生成新密钥
        String newKeyId = generateNewKey();
        
        // 2. 使用新密钥重新加密数据
        reEncryptData(newKeyId);
        
        // 3. 通知服务端更新密钥
        notifyServerKeyRotation(newKeyId);
        
        // 4. 删除旧密钥
        deleteOldKey();
        
        // 5. 更新密钥创建时间
        updateKeyCreationTime();
    }
    
    /**
     * 双密钥过渡期
     * 在轮换期间同时支持新旧密钥
     */
    public byte[] decryptWithFallback(byte[] data) {
        try {
            // 尝试用新密钥解密
            return decryptWithNewKey(data);
        } catch (Exception e) {
            // 回退到旧密钥
            return decryptWithOldKey(data);
        }
    }
}

14.5 运行时保护

14.5.1 环境检测

/**
 * 运行环境安全检测
 */
public class EnvironmentChecker {
    
    /**
     * 综合环境检测
     */
    public SecurityCheckResult checkEnvironment(Context context) {
        SecurityCheckResult result = new SecurityCheckResult();
        
        // 1. Root检测
        result.isRooted = checkRoot();
        
        // 2. 模拟器检测
        result.isEmulator = checkEmulator();
        
        // 3. 调试检测
        result.isDebugged = checkDebug(context);
        
        // 4. Hook框架检测
        result.hasHookFramework = checkHookFramework();
        
        // 5. 签名校验
        result.isSignatureValid = checkSignature(context);
        
        // 6. 安装来源检测
        result.isFromTrustedSource = checkInstallSource(context);
        
        return result;
    }
    
    /**
     * Root检测
     */
    private boolean checkRoot() {
        // 检查常见Root文件
        String[] rootPaths = {
            "/system/bin/su",
            "/system/xbin/su",
            "/sbin/su",
            "/data/local/xbin/su",
            "/data/local/bin/su",
            "/system/app/Superuser.apk",
            "/system/app/SuperSU.apk"
        };
        
        for (String path : rootPaths) {
            if (new File(path).exists()) {
                return true;
            }
        }
        
        // 检查Magisk
        if (new File("/sbin/.magisk").exists() ||
            new File("/data/adb/magisk").exists()) {
            return true;
        }
        
        // 检查su命令
        try {
            Process process = Runtime.getRuntime().exec("which su");
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()));
            if (reader.readLine() != null) {
                return true;
            }
        } catch (Exception e) {
            // ignore
        }
        
        return false;
    }
    
    /**
     * 模拟器检测
     */
    private boolean checkEmulator() {
        // 检查Build属性
        if (Build.FINGERPRINT.contains("generic") ||
            Build.FINGERPRINT.contains("unknown") ||
            Build.MODEL.contains("google_sdk") ||
            Build.MODEL.contains("Emulator") ||
            Build.MODEL.contains("Android SDK built for x86") ||
            Build.MANUFACTURER.contains("Genymotion") ||
            Build.HARDWARE.contains("goldfish") ||
            Build.HARDWARE.contains("ranchu") ||
            Build.PRODUCT.contains("sdk") ||
            Build.PRODUCT.contains("vbox86p") ||
            Build.BOARD.contains("unknown")) {
            return true;
        }
        
        // 检查特征文件
        String[] emulatorFiles = {
            "/dev/socket/qemud",
            "/dev/qemu_pipe",
            "/system/lib/libc_malloc_debug_qemu.so",
            "/sys/qemu_trace",
            "/system/bin/qemu-props"
        };
        
        for (String file : emulatorFiles) {
            if (new File(file).exists()) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 调试检测
     */
    private boolean checkDebug(Context context) {
        // 检查调试标志
        if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
            return true;
        }
        
        // 检查调试器连接
        if (Debug.isDebuggerConnected()) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Hook框架检测
     */
    private boolean checkHookFramework() {
        // 检查Xposed
        try {
            Class.forName("de.robv.android.xposed.XposedBridge");
            return true;
        } catch (ClassNotFoundException e) {
            // not found
        }
        
        // 检查Frida
        try {
            // 检查Frida特征端口
            Socket socket = new Socket();
            socket.connect(new InetSocketAddress("127.0.0.1", 27042), 100);
            socket.close();
            return true;
        } catch (Exception e) {
            // not found
        }
        
        // 检查堆栈中的Hook痕迹
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement element : stackTrace) {
            String className = element.getClassName();
            if (className.contains("xposed") || 
                className.contains("frida") ||
                className.contains("substrate")) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 签名校验
     */
    private boolean checkSignature(Context context) {
        try {
            PackageInfo packageInfo = context.getPackageManager()
                .getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
            
            for (Signature signature : packageInfo.signatures) {
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                byte[] hash = md.digest(signature.toByteArray());
                String signatureHash = bytesToHex(hash);
                
                // 与预期签名比较
                if (signatureHash.equals(EXPECTED_SIGNATURE)) {
                    return true;
                }
            }
        } catch (Exception e) {
            // error
        }
        
        return false;
    }
    
    private static final String EXPECTED_SIGNATURE = "...";
    
    /**
     * 安全检测结果
     */
    public static class SecurityCheckResult {
        public boolean isRooted;
        public boolean isEmulator;
        public boolean isDebugged;
        public boolean hasHookFramework;
        public boolean isSignatureValid;
        public boolean isFromTrustedSource;
        
        public boolean isSecure() {
            return !isRooted && !isEmulator && !isDebugged && 
                   !hasHookFramework && isSignatureValid && isFromTrustedSource;
        }
    }
}

14.6 安全开发流程

14.6.1 安全开发生命周期

┌─────────────────────────────────────────────────────────────────┐
│                    安全开发生命周期 (SDL)                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  阶段              安全活动                                      │
│  ─────────────────────────────────────────────────────────────  │
│  需求阶段          • 安全需求分析                                │
│                   • 威胁建模                                     │
│                   • 安全目标定义                                 │
│                                                                  │
│  设计阶段          • 安全架构设计                                │
│                   • 攻击面分析                                   │
│                   • 安全设计评审                                 │
│                                                                  │
│  开发阶段          • 安全编码规范                                │
│                   • 代码安全审查                                 │
│                   • 静态代码分析                                 │
│                                                                  │
│  测试阶段          • 安全测试                                    │
│                   • 渗透测试                                     │
│                   • 漏洞扫描                                     │
│                                                                  │
│  发布阶段          • 安全配置检查                                │
│                   • 最终安全评审                                 │
│                   • 应急响应计划                                 │
│                                                                  │
│  运维阶段          • 安全监控                                    │
│                   • 漏洞响应                                     │
│                   • 安全更新                                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

14.6.2 安全编码规范

/**
 * 安全编码规范示例
 */
public class SecureCodingGuidelines {
    
    // ==================== 输入验证 ====================
    
    /**
     * 规则1: 始终验证输入
     */
    public void validateInput(String input) {
        // 检查null
        if (input == null) {
            throw new IllegalArgumentException("Input cannot be null");
        }
        
        // 检查长度
        if (input.length() > MAX_LENGTH) {
            throw new IllegalArgumentException("Input too long");
        }
        
        // 检查格式
        if (!input.matches(VALID_PATTERN)) {
            throw new IllegalArgumentException("Invalid input format");
        }
        
        // 检查特殊字符
        input = sanitize(input);
    }
    
    // ==================== 敏感数据处理 ====================
    
    /**
     * 规则2: 敏感数据使用后立即清除
     */
    public void handleSensitiveData() {
        char[] password = getPassword();
        try {
            // 使用密码
            authenticate(password);
        } finally {
            // 清除密码
            Arrays.fill(password, '\0');
        }
    }
    
    /**
     * 规则3: 不要在日志中记录敏感信息
     */
    public void secureLogging(String userId, String password) {
        // 错误示例
        // Log.d(TAG, "Login: user=" + userId + ", password=" + password);
        
        // 正确示例
        Log.d(TAG, "Login attempt for user: " + maskUserId(userId));
    }
    
    // ==================== 加密使用 ====================
    
    /**
     * 规则4: 使用安全的加密算法
     */
    public void secureEncryption() {
        // 错误: 使用弱算法
        // Cipher.getInstance("DES");
        // Cipher.getInstance("AES/ECB/PKCS5Padding");
        
        // 正确: 使用强算法
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    }
    
    /**
     * 规则5: 使用安全的随机数
     */
    public byte[] generateSecureRandom(int length) {
        // 错误: 使用不安全的随机数
        // Random random = new Random();
        
        // 正确: 使用安全随机数
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[length];
        secureRandom.nextBytes(bytes);
        return bytes;
    }
    
    // ==================== 异常处理 ====================
    
    /**
     * 规则6: 不要泄露敏感信息到异常
     */
    public void secureExceptionHandling() {
        try {
            // 业务逻辑
        } catch (Exception e) {
            // 错误: 暴露内部信息
            // throw new RuntimeException("Database error: " + e.getMessage());
            
            // 正确: 记录详细日志,返回通用错误
            Log.e(TAG, "Internal error", e);
            throw new RuntimeException("An error occurred. Please try again.");
        }
    }
}

14.7 本章小结

本章我们从防御者的角度,系统性地介绍了移动应用安全设计的最佳实践:

14.7.1 技术要点回顾

┌─────────────────────────────────────────────────────────────────┐
│                      本章技术要点                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. 安全架构设计                                                 │
│     • 纵深防御原则                                               │
│     • 最小权限原则                                               │
│     • 安全默认值                                                 │
│                                                                  │
│  2. 网络安全                                                     │
│     • 传输层安全                                                 │
│     • 请求签名设计                                               │
│     • 数据加密传输                                               │
│                                                                  │
│  3. 代码保护                                                     │
│     • 混淆策略                                                   │
│     • 关键代码Native化                                           │
│                                                                  │
│  4. 密钥管理                                                     │
│     • 安全存储方案                                               │
│     • Android Keystore                                          │
│     • 密钥轮换策略                                               │
│                                                                  │
│  5. 运行时保护                                                   │
│     • 环境检测                                                   │
│     • 完整性校验                                                 │
│                                                                  │
│  6. 安全开发流程                                                 │
│     • 安全开发生命周期                                           │
│     • 安全编码规范                                               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

14.7.2 下一章预告

在下一章《逆向工程方法论》中,我们将总结逆向工程的思维方法和实践经验:

  • 逆向工程的思维模式
  • 问题分析方法
  • 工具选择策略
  • 经验总结

本章完