# 安全最佳实践

Android应用安全是开发过程中必须重视的方面。本文档提供Android应用安全开发的最佳实践指南。

## 目录

- [安全开发原则](#安全开发原则)
- [数据安全](#数据安全)
- [网络安全](#网络安全)
- [代码安全](#代码安全)
- [安全测试](#安全测试)

---

## 安全开发原则

### 1. 最小权限原则

只申请和使用应用真正需要的权限，避免过度申请权限。

#### 权限申请原则

```java
// ❌ 错误：申请不必要的权限
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_SMS" />
// 如果应用不需要这些功能，不应该申请

// ✅ 正确：只申请必要的权限
<uses-permission android:name="android.permission.CAMERA" />
// 只在需要相机功能时申请
```

#### 运行时权限检查

```java
// 使用权限前检查
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
        != PackageManager.PERMISSION_GRANTED) {
    // 解释为什么需要权限
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.CAMERA)) {
        // 显示解释对话框
        showPermissionRationale();
    } else {
        // 直接申请权限
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CAMERA},
                REQUEST_CODE_CAMERA);
    }
} else {
    // 权限已授予，使用功能
    useCamera();
}
```

### 2. 纵深防御

采用多层安全防护机制，不依赖单一安全措施。

#### 多层防护示例

```java
// 第一层：权限检查
if (checkPermission()) {
    // 第二层：输入验证
    if (validateInput(data)) {
        // 第三层：数据加密
        String encrypted = encrypt(data);
        // 第四层：安全存储
        saveSecurely(encrypted);
    }
}
```

### 3. 安全设计

从设计阶段就考虑安全问题，而不是事后补救。

#### 安全设计要点

- **威胁建模**：识别潜在的安全威胁
- **安全架构**：设计安全的应用架构
- **安全编码**：遵循安全编码规范
- **安全测试**：进行安全测试和审计

### 4. 数据最小化

只收集和存储必要的数据，及时删除不需要的数据。

```java
// ❌ 错误：收集过多数据
class User {
    String name;
    String email;
    String phone;
    String address;
    String idCard;  // 不需要身份证号
    String bankCard; // 不需要银行卡号
}

// ✅ 正确：只收集必要数据
class User {
    String name;
    String email;
    // 只在需要时收集其他信息
}
```

### 5. 默认安全

默认采用安全配置，而不是默认开放。

```xml
<!-- ❌ 错误：默认导出组件 -->
<activity
    android:name=".SensitiveActivity"
    android:exported="true" />

<!-- ✅ 正确：默认不导出，需要时显式设置 -->
<activity
    android:name=".SensitiveActivity"
    android:exported="false" />
```

---

## 数据安全

### 1. 敏感数据加密

对敏感数据进行加密存储，包括密码、密钥、个人信息等。

#### 使用Android密钥库

```java
// 使用Android密钥库存储密钥
public class KeyStoreHelper {
    private static final String KEYSTORE_ALIAS = "my_key";
    private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
    
    public static void generateKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE);
        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
                KEYSTORE_ALIAS,
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .build();
        keyGenerator.init(spec);
        keyGenerator.generateKey();
    }
    
    public static byte[] encrypt(String data) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
        keyStore.load(null);
        KeyStore.SecretKeyEntry secretKeyEntry = 
                (KeyStore.SecretKeyEntry) keyStore.getEntry(KEYSTORE_ALIAS, null);
        SecretKey secretKey = secretKeyEntry.getSecretKey();
        
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        
        byte[] iv = cipher.getIV();
        byte[] encrypted = cipher.doFinal(data.getBytes());
        
        // 将IV和加密数据组合
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        outputStream.write(iv);
        outputStream.write(encrypted);
        return outputStream.toByteArray();
    }
}
```

#### 数据加密存储

```java
// 加密敏感数据
public class SecureStorage {
    public static void saveEncryptedData(String key, String value) {
        try {
            byte[] encrypted = KeyStoreHelper.encrypt(value);
            // 存储加密后的数据
            SharedPreferences prefs = getSharedPreferences("secure_prefs", MODE_PRIVATE);
            String base64 = Base64.encodeToString(encrypted, Base64.DEFAULT);
            prefs.edit().putString(key, base64).apply();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static String getDecryptedData(String key) {
        try {
            SharedPreferences prefs = getSharedPreferences("secure_prefs", MODE_PRIVATE);
            String base64 = prefs.getString(key, null);
            if (base64 == null) return null;
            
            byte[] encrypted = Base64.decode(base64, Base64.DEFAULT);
            // 解密数据
            return KeyStoreHelper.decrypt(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
```

### 2. 安全存储

使用安全的存储方式，避免明文存储敏感信息。

#### SharedPreferences安全使用

```java
// ❌ 错误：明文存储敏感信息
SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
prefs.edit().putString("password", password).apply();

// ✅ 正确：加密后存储
String encryptedPassword = encrypt(password);
prefs.edit().putString("password", encryptedPassword).apply();
```

#### 使用EncryptedSharedPreferences（Android 6.0+）

```java
// 使用EncryptedSharedPreferences
MasterKey masterKey = new MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build();

SharedPreferences encryptedPrefs = EncryptedSharedPreferences.create(
        context,
        "encrypted_prefs",
        masterKey,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

encryptedPrefs.edit()
        .putString("sensitive_data", data)
        .apply();
```

### 3. 数据清理

及时清理不再需要的敏感数据。

```java
// 清理敏感数据
public void clearSensitiveData() {
    // 清理内存中的数据
    password = null;
    apiKey = null;
    
    // 清理SharedPreferences
    SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
    prefs.edit().clear().apply();
    
    // 清理文件
    File sensitiveFile = new File(getFilesDir(), "sensitive.txt");
    if (sensitiveFile.exists()) {
        // 安全删除：覆盖后删除
        secureDelete(sensitiveFile);
    }
}

// 安全删除文件
private void secureDelete(File file) {
    if (file.exists()) {
        // 覆盖文件内容
        RandomAccessFile raf = new RandomAccessFile(file, "rws");
        raf.setLength(0);
        raf.close();
        // 删除文件
        file.delete();
    }
}
```

### 4. 日志安全

避免在日志中输出敏感信息。

```java
// ❌ 错误：日志中输出敏感信息
Log.d("TAG", "User password: " + password);
Log.d("TAG", "API key: " + apiKey);
Log.d("TAG", "User token: " + token);

// ✅ 正确：敏感信息脱敏或移除
Log.d("TAG", "User login attempt");
// 或使用条件编译
if (BuildConfig.DEBUG) {
    Log.d("TAG", "Debug info: " + debugInfo);
}
```

### 5. 内存安全

避免在内存中长时间保存敏感数据。

```java
// 使用后立即清理
public void processSensitiveData(String data) {
    try {
        // 处理数据
        process(data);
    } finally {
        // 清理内存
        data = null;
        System.gc(); // 建议垃圾回收
    }
}

// 使用char[]而不是String存储密码（可被清理）
public void handlePassword(char[] password) {
    try {
        // 使用密码
        authenticate(password);
    } finally {
        // 清理密码
        Arrays.fill(password, '\0');
    }
}
```

---

## 网络安全

### 1. HTTPS通信

使用HTTPS进行网络通信，避免HTTP明文传输。

#### 配置网络安全

```xml
<!-- res/xml/network_security_config.xml -->
<network-security-config>
    <!-- 默认配置：只允许HTTPS -->
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
    
    <!-- 特定域名配置 -->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="system" />
            <!-- 自定义证书 -->
            <certificates src="@raw/my_ca" />
        </trust-anchors>
    </domain-config>
</network-security-config>
```

```xml
<!-- AndroidManifest.xml -->
<application
    android:networkSecurityConfig="@xml/network_security_config"
    ... />
```

#### 证书固定（Certificate Pinning）

```xml
<!-- 证书固定配置 -->
<domain-config>
    <domain includeSubdomains="true">api.example.com</domain>
    <pin-set expiration="2025-12-31">
        <!-- SHA-256指纹 -->
        <pin digest="SHA-256">base64==</pin>
        <!-- 备用证书 -->
        <pin digest="SHA-256">base64==</pin>
    </pin-set>
</domain-config>
```

### 2. 输入验证

验证所有网络输入，防止注入攻击。

```java
// 输入验证
public class InputValidator {
    // 验证URL
    public static boolean isValidUrl(String url) {
        if (url == null || url.isEmpty()) {
            return false;
        }
        try {
            URL urlObj = new URL(url);
            // 只允许HTTPS
            return "https".equals(urlObj.getProtocol());
        } catch (MalformedURLException e) {
            return false;
        }
    }
    
    // 验证输入长度
    public static boolean isValidLength(String input, int maxLength) {
        return input != null && input.length() <= maxLength;
    }
    
    // 验证输入格式
    public static boolean isValidEmail(String email) {
        if (email == null || email.isEmpty()) {
            return false;
        }
        String pattern = "^[A-Za-z0-9+_.-]+@(.+)$";
        return email.matches(pattern);
    }
    
    // 防止SQL注入
    public static String sanitizeSql(String input) {
        if (input == null) return "";
        // 转义特殊字符
        return input.replace("'", "''")
                    .replace(";", "")
                    .replace("--", "");
    }
}
```

### 3. 请求签名

对重要请求进行签名，防止篡改。

```java
// 请求签名
public class RequestSigner {
    public static String signRequest(String data, String secret) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(
                    secret.getBytes(), "HmacSHA256");
            mac.init(secretKey);
            byte[] hash = mac.doFinal(data.getBytes());
            return Base64.encodeToString(hash, Base64.NO_WRAP);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    public static boolean verifyRequest(String data, String signature, String secret) {
        String calculatedSignature = signRequest(data, secret);
        return signature != null && signature.equals(calculatedSignature);
    }
}
```

### 4. 超时和重试

设置合理的超时和重试机制。

```java
// 配置OkHttp超时
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .writeTimeout(30, TimeUnit.SECONDS)
        .build();
```

### 5. 敏感信息传输

避免在URL或请求头中传输敏感信息。

```java
// ❌ 错误：敏感信息在URL中
String url = "https://api.example.com/login?username=user&password=pass";

// ✅ 正确：使用POST请求体
RequestBody body = new FormBody.Builder()
        .add("username", username)
        .add("password", password)
        .build();
Request request = new Request.Builder()
        .url("https://api.example.com/login")
        .post(body)
        .build();
```

---

## 代码安全

### 1. 组件安全

正确配置组件权限和导出状态。

#### Activity安全配置

```xml
<!-- AndroidManifest.xml -->
<!-- ❌ 错误：导出且无权限保护 -->
<activity
    android:name=".SensitiveActivity"
    android:exported="true" />

<!-- ✅ 正确：不导出或设置权限 -->
<activity
    android:name=".SensitiveActivity"
    android:exported="false"
    android:permission="com.example.PERMISSION" />
```

#### Service安全配置

```xml
<!-- Service安全配置 -->
<service
    android:name=".MyService"
    android:exported="false"
    android:permission="com.example.SERVICE_PERMISSION" />
```

#### BroadcastReceiver安全配置

```xml
<!-- 动态注册更安全 -->
<!-- 静态注册需要设置权限 -->
<receiver
    android:name=".MyReceiver"
    android:exported="false"
    android:permission="com.example.BROADCAST_PERMISSION">
    <intent-filter>
        <action android:name="com.example.ACTION" />
    </intent-filter>
</receiver>
```

#### ContentProvider安全配置

```xml
<!-- ContentProvider安全配置 -->
<provider
    android:name=".MyProvider"
    android:authorities="com.example.provider"
    android:exported="false"
    android:readPermission="com.example.READ_PERMISSION"
    android:writePermission="com.example.WRITE_PERMISSION" />
```

### 2. Intent安全

验证Intent的来源和内容。

```java
// Intent安全处理
public class IntentValidator {
    // 验证Intent来源
    public static boolean isValidIntent(Intent intent) {
        // 检查组件是否存在
        ComponentName component = intent.getComponent();
        if (component == null) {
            return false;
        }
        
        // 验证包名
        String packageName = component.getPackageName();
        if (!packageName.equals(getPackageName())) {
            return false;
        }
        
        return true;
    }
    
    // 验证Intent数据
    public static boolean isValidIntentData(Intent intent) {
        Uri data = intent.getData();
        if (data == null) {
            return false;
        }
        
        // 验证scheme
        String scheme = data.getScheme();
        if (!"https".equals(scheme) && !"content".equals(scheme)) {
            return false;
        }
        
        return true;
    }
    
    // 安全启动Activity
    public static void startActivitySafely(Context context, Intent intent) {
        if (isValidIntent(intent) && isValidIntentData(intent)) {
            context.startActivity(intent);
        } else {
            // 记录安全事件
            Log.w("Security", "Invalid intent blocked");
        }
    }
}
```

### 3. 代码混淆

使用ProGuard或R8进行代码混淆。

#### ProGuard配置

```proguard
# proguard-rules.pro

# 保留必要的类
-keep class com.example.model.** { *; }

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

# 混淆配置
-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
```

#### build.gradle配置

```gradle
android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
}
```

### 4. 反调试保护

防止应用被调试和分析。

```java
// 检测调试器
public class AntiDebug {
    public static boolean isDebugging() {
        // 检查调试标志
        if (Debug.isDebuggerConnected()) {
            return true;
        }
        
        // 检查tracerpid
        try {
            BufferedReader reader = new BufferedReader(
                    new FileReader("/proc/self/status"));
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("TracerPid:")) {
                    int tracerPid = Integer.parseInt(line.split("\\s+")[1]);
                    if (tracerPid != 0) {
                        return true;
                    }
                }
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return false;
    }
    
    // 检测模拟器
    public static boolean isEmulator() {
        return Build.FINGERPRINT.startsWith("generic")
                || Build.FINGERPRINT.startsWith("unknown")
                || Build.MODEL.contains("google_sdk")
                || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86")
                || Build.MANUFACTURER.contains("Genymotion")
                || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT);
    }
}
```

### 5. 输入验证

验证所有用户输入，防止注入攻击。

```java
// 输入验证工具
public class InputValidator {
    // 验证文件名
    public static boolean isValidFileName(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return false;
        }
        // 防止路径遍历
        if (fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) {
            return false;
        }
        return true;
    }
    
    // 验证JSON
    public static boolean isValidJson(String json) {
        try {
            new JSONObject(json);
            return true;
        } catch (JSONException e) {
            return false;
        }
    }
    
    // 验证XML
    public static boolean isValidXml(String xml) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            factory.setNamespaceAware(true);
            XmlPullParser parser = factory.newPullParser();
            parser.setInput(new StringReader(xml));
            while (parser.next() != XmlPullParser.END_DOCUMENT) {
                // 解析XML
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
```

---

## 安全测试

### 1. 静态代码分析

使用工具进行静态代码分析，发现潜在安全问题。

#### 使用工具

- **Android Lint**：Android官方代码检查工具
- **FindBugs/SpotBugs**：Java代码缺陷检测
- **SonarQube**：代码质量分析平台
- **Checkmarx**：安全漏洞扫描

#### Lint配置

```gradle
android {
    lintOptions {
        // 将警告视为错误
        warningsAsErrors true
        // 检查所有问题
        checkAllWarnings true
        // 忽略某些警告
        disable 'UnusedResources'
        // 输出报告
        htmlReport true
        htmlOutput file("$project.buildDir/reports/lint/lint.html")
    }
}
```

### 2. 动态安全测试

在运行时测试应用的安全性。

#### 权限测试

```java
// 测试权限检查
@Test
public void testPermissionCheck() {
    // 测试权限未授予时的行为
    when(ContextCompat.checkSelfPermission(any(), anyString()))
            .thenReturn(PackageManager.PERMISSION_DENIED);
    
    // 验证应用正确处理权限拒绝
    assertFalse(hasPermission());
}
```

#### 网络安全测试

```java
// 测试HTTPS连接
@Test
public void testHttpsConnection() {
    // 验证只使用HTTPS
    String url = getApiUrl();
    assertTrue(url.startsWith("https://"));
    
    // 测试证书验证
    // ...
}
```

### 3. 渗透测试

模拟攻击者进行渗透测试。

#### 测试项目

- **组件导出测试**：检查是否有不必要的组件导出
- **权限测试**：测试权限是否正确配置
- **数据泄露测试**：检查是否有敏感信息泄露
- **注入攻击测试**：测试SQL注入、XSS等
- **加密测试**：验证数据是否正确加密

### 4. 安全审计

定期进行安全审计。

#### 审计清单

- [ ] 权限使用是否合理
- [ ] 敏感数据是否加密
- [ ] 网络通信是否使用HTTPS
- [ ] 组件是否正确配置
- [ ] 输入是否验证
- [ ] 日志是否包含敏感信息
- [ ] 代码是否混淆
- [ ] 依赖库是否安全

### 5. 漏洞扫描

使用自动化工具扫描已知漏洞。

#### 工具推荐

- **OWASP Mobile Top 10**：移动应用安全风险清单
- **MobSF (Mobile Security Framework)**：移动应用安全测试框架
- **QARK (Quick Android Review Kit)**：Android应用安全分析工具

---

## 总结

Android应用安全是一个持续的过程，需要：

1. **遵循安全原则**：最小权限、纵深防御、安全设计
2. **保护数据安全**：加密存储、安全传输、及时清理
3. **确保网络安全**：HTTPS通信、输入验证、请求签名
4. **加强代码安全**：组件安全、代码混淆、输入验证
5. **进行安全测试**：静态分析、动态测试、渗透测试

建议：
- 定期进行安全审计
- 关注安全更新和漏洞公告
- 使用官方推荐的安全实践
- 参与安全社区，学习最新安全知识

---

*最后更新：2024年*
