2024 桐庐半程马拉松
00:00:00
时间
0.00
距离(公里)
--:--
配速
--
步频
--
心率 (bpm)
--
配速
步频
|
share-image
ESC

Android APP 安全防护完整实施指南

1. 代码混淆与加固

1.1 启用代码混淆

修改 pp/build.gradle.kts 文件:

android {
buildTypes {
release {
isMinifyEnabled = true // 启用代码混淆
isShrinkResources = true // 启用资源压缩
proguardFiles(
getDefaultProguardFile(\
proguard-android-optimize.txt\),
\proguard-rules.pro\
)
}
debug {
isMinifyEnabled = false
}
}
}

1.2 配置ProGuard规则

创建或修改 pp/proguard-rules.pro 文件:

# 基础配置
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
-keepattributes *Annotation*
-keepattributes Signature
-keepattributes Exceptions

# Kotlin配置
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class ** {
<fields>;
}

# Kotlin Coroutines
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
-dontwarn kotlinx.coroutines.**

# Jetpack Compose
-keep class androidx.compose.** { *; }
-dontwarn androidx.compose.**

# Retrofit & OkHttp
-keepattributes Signature, InnerClasses, EnclosingMethod
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
-keepclassmembers,allowshrinking,allowobfuscation interface * {
@retrofit2.http.* <methods>;
}
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn javax.annotation.**
-dontwarn kotlin.Unit
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*

# OkHttp
-dontwarn okhttp3.**
-dontwarn okio.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

# Gson
-keepattributes Signature
-keepattributes *Annotation*
-dontwarn sun.misc.**
-keep class com.google.gson.** { *; }
-keep class * implements com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# 保留数据模型类
-keep class com.awen.running.network.** { *; }
-keep class com.awen.running.data.** { *; }

# 高德地图 SDK
-keep class com.amap.api.** { *; }
-keep class com.autonavi.** { *; }
-dontwarn com.amap.api.**
-dontwarn com.autonavi.**

# 阿里云 NUI SDK
-keep class com.alibaba.** { *; }
-dontwarn com.alibaba.**

# FastJSON
-keep class com.alibaba.fastjson.** { *; }
-dontwarn com.alibaba.fastjson.**

# MPAndroidChart
-keep class com.github.mikephil.charting.** { *; }
-dontwarn com.github.mikephil.charting.**

# 蓝牙相关
-keep class android.bluetooth.** { *; }
-keep class com.awen.running.bluetooth.** { *; }

# 混淆敏感信息
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** i(...);
}

# 保留Serializable类
-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();
}

# 保留Parcelable类
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable *;
}

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

2. 网络通信安全

2.1 配置SSL证书锁定

修改 pp/src/main/java/com/awen/running/network/RetrofitClient.kt 文件:

package com.awen.running.network

import android.content.Context
import android.content.SharedPreferences
import com.awen.running.BuildConfig
import com.awen.running.security.EncryptedStorage
import okhttp3.CertificatePinner
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

object RetrofitClient {
private const val BASE_URL = \https://api.awen.me/api/v1/\

// 不再使用普通的SharedPreferences,改为加密存储
private var authToken: String? = null
private var userId: Int? = null

private var applicationContext: Context? = null

/**
* 初始化(在Application中调用)
*/
fun init(context: Context) {
applicationContext = context.applicationContext
// 从加密存储加载Token
authToken = EncryptedStorage.getToken(context)
userId = EncryptedStorage.getUserId(context)
}

/**
* SSL证书锁定(防止中间人攻击)
*/
private val certificatePinner = CertificatePinner.Builder()
.add(\api.awen.me\, \sha256/v9InP1GnrRxH80k4Mrccvr2JwZEsrJh+LVyOGzcZmk4=\) // 真实证书指纹
.add(\api.awen.me\, \sha256/mTGuGxOfs9PK15aUeNsmQQQ6VL8f0bCvdvEEqkjC6M8=\) // 备用证书
.build()

private val loggingInterceptor = HttpLoggingInterceptor().apply {
// 只在Debug版本中启用详细日志,防止敏感信息泄露
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE // Release版本关闭日志
}
}

private val client: OkHttpClient by lazy {
OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
// 启用SSL证书锁定(生产环境强烈推荐)
.certificatePinner(certificatePinner) // 启用证书锁定
.addInterceptor(loggingInterceptor)
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.header(\Content-Type\, \application/json\)

// 添加Authorization头
authToken?.let {
requestBuilder.header(\Authorization\, \Bearer
\)
}

chain.proceed(requestBuilder.build())
}
.build()
}

val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}

/**
* 保存Token
*/
fun setToken(token: String, userId: Int? = null) {
authToken = token
this.userId = userId
// 同时保存到加密存储
applicationContext?.let { context ->
EncryptedStorage.saveToken(context, token)
userId?.let { EncryptedStorage.saveUserId(context, it) }
}
}

/**
* 获取Token
*/
fun getToken(): String? = authToken

/**
* 获取用户ID
*/
fun getUserId(): Int? = userId

/**
* 是否有Token
*/
fun hasToken(): Boolean = !authToken.isNullOrEmpty()

/**
* 清除Token(登出时调用)
*/
fun clearToken() {
authToken = null
userId = null
// 同时清除加密存储
applicationContext?.let { context ->
EncryptedStorage.clearToken(context)
}
}
}

2.2 网络安全配置

创建 pp/src/main/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>

<!-- 仅允许HTTPS -->
<domain-config cleartextTrafficPermitted=\false\>
<domain includeSubdomains=\true\>api.awen.me</domain>
</domain-config>
</network-security-config>

在 pp/src/main/AndroidManifest.xml 中引用:

<application
android:networkSecurityConfig=\@xml/network_security_config\
...>

3. 数据存储安全

3.1 添加加密存储依赖

修改 pp/build.gradle.kts 文件:

dependencies {
// 其他依赖...

// 加密存储
implementation(\androidx.security:security-crypto:1.1.0-alpha06\)
}

3.2 创建加密存储工具类

创建 pp/src/main/java/com/awen/running/security/EncryptedStorage.kt 文件:

package com.awen.running.security

import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
import android.util.Base64

/**
* 加密存储工具类 - 用于安全存储敏感数据
*/
object EncryptedStorage {

private const val ENCRYPTED_PREFS_NAME = \encrypted_prefs\
private const val ANDROID_KEYSTORE = \AndroidKeyStore\
private const val AES_MODE = \AES/GCM/NoPadding\

/**
* 获取加密SharedPreferences实例
*/
private fun getEncryptedPrefs(context: Context): SharedPreferences {
val masterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()

return EncryptedSharedPreferences.create(
context,
ENCRYPTED_PREFS_NAME,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}

// ==================== JWT Token 操作 ====================

private const val KEY_JWT_TOKEN = \jwt_token\
private const val KEY_USER_ID = \user_id\

/**
* 保存JWT Token
*/
fun saveToken(context: Context, token: String) {
getEncryptedPrefs(context).edit()
.putString(KEY_JWT_TOKEN, token)
.apply()
}

/**
* 获取JWT Token
*/
fun getToken(context: Context): String? {
return getEncryptedPrefs(context).getString(KEY_JWT_TOKEN, null)
}

/**
* 保存用户ID
*/
fun saveUserId(context: Context, userId: Int) {
getEncryptedPrefs(context).edit()
.putInt(KEY_USER_ID, userId)
.apply()
}

/**
* 获取用户ID
*/
fun getUserId(context: Context): Int? {
return getEncryptedPrefs(context).getInt(KEY_USER_ID, -1).takeIf { it != -1 }
}

/**
* 清除Token
*/
fun clearToken(context: Context) {
getEncryptedPrefs(context).edit()
.remove(KEY_JWT_TOKEN)
.remove(KEY_USER_ID)
.apply()
}

/**
* 检查是否有有效Token
*/
fun hasValidToken(context: Context): Boolean {
val token = getToken(context)
return !token.isNullOrEmpty()
}

// ==================== 其他敏感数据操作 ====================

private const val KEY_RESTING_HEART_RATE = \resting_heart_rate\
private const val KEY_HEART_RATE_ZONE_SETTINGS = \heart_rate_zone_settings\

/**
* 保存静息心率
*/
fun saveRestingHeartRate(context: Context, restingRate: Int) {
getEncryptedPrefs(context).edit()
.putInt(KEY_RESTING_HEART_RATE, restingRate)
.apply()
}

/**
* 获取静息心率
*/
fun getRestingHeartRate(context: Context): Int? {
val rate = getEncryptedPrefs(context).getInt(KEY_RESTING_HEART_RATE, -1)
return if (rate != -1) rate else null
}

/**
* 保存心率区间设置
*/
fun saveHeartRateZoneSettings(context: Context, settings: String) {
getEncryptedPrefs(context).edit()
.putString(KEY_HEART_RATE_ZONE_SETTINGS, settings)
.apply()
}

/**
* 获取心率区间设置
*/
fun getHeartRateZoneSettings(context: Context): String? {
return getEncryptedPrefs(context).getString(KEY_HEART_RATE_ZONE_SETTINGS, null)
}

/**
* 清除所有加密数据
*/
fun clearAll(context: Context) {
getEncryptedPrefs(context).edit()
.clear()
.apply()
}
}

4. 应用签名与发布

4.1 生成签名密钥

在命令行中执行以下命令:

keytool -genkey -v -keystore running-release.keystore \
-alias running-key \
-keyalg RSA \
-keysize 2048 \
-validity 10000

4.2 配置签名

修改 pp/build.gradle.kts 文件:

android {
signingConfigs {
create(\release\) {
storeFile = file(rootDir.absolutePath + \/running-release.keystore\)
storePassword = System.getenv(\KEYSTORE_PASSWORD\) ?: System.getProperty(\KEYSTORE_PASSWORD\)
keyAlias = \running-key\
keyPassword = System.getenv(\KEY_PASSWORD\) ?: System.getProperty(\KEY_PASSWORD\)
}
}

buildTypes {
release {
signingConfig = signingConfigs.getByName(\release\)
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile(\proguard-android-optimize.txt\),
\proguard-rules.pro\
)
}
debug {
isMinifyEnabled = false
}
}
}

4.3 安全构建命令

生产环境

KEYSTORE_PASSWORD=your_password KEY_PASSWORD=your_password ./gradlew assembleRelease

PowerShell环境

=\your_password\; =\your_password\; ./gradlew assembleRelease

本地开发环境

./gradlew assembleRelease -DKEYSTORE_PASSWORD=your_password -DKEY_PASSWORD=your_password

5. 运行时安全检测

5.1 创建安全检测工具类

创建 pp/src/main/java/com/awen/running/security/SecurityUtils.kt 文件:

package com.awen.running.security

import android.content.Context
import android.os.Build
import android.provider.Settings
import java.io.File

/**
* 安全工具类:Root检测、模拟器检测、防调试
*/
object SecurityUtils {

/**
* 检测设备是否被Root
* @return true表示已Root,false表示未Root
*/
fun isDeviceRooted(): Boolean {
return checkRootMethod1() || checkRootMethod2() || checkRootMethod3()
}

/**
* 方法1:检查常见的Root管理工具
*/
private fun checkRootMethod1(): Boolean {
val paths = arrayOf(
\/system/app/Superuser.apk\,
\/sbin/su\,
\/system/bin/su\,
\/system/xbin/su\,
\/data/local/xbin/su\,
\/data/local/bin/su\,
\/system/sd/xbin/su\,
\/system/bin/failsafe/su\,
\/data/local/su\,
\/su/bin/su\
)

for (path in paths) {
if (File(path).exists()) {
return true
}
}
return false
}

/**
* 方法2:执行su命令检测
*/
private fun checkRootMethod2(): Boolean {
var process: Process? = null
return try {
process = Runtime.getRuntime().exec(arrayOf(\/system/xbin/which\, \su\))
val bufferedReader = process.inputStream.bufferedReader()
bufferedReader.readLine() != null
} catch (e: Exception) {
false
} finally {
process?.destroy()
}
}

/**
* 方法3:检查系统属性
*/
private fun checkRootMethod3(): Boolean {
val buildTags = Build.TAGS
return buildTags != null && buildTags.contains(\test-keys\)
}

/**
* 检测是否在模拟器中运行
* @return true表示是模拟器,false表示真实设备
*/
fun isEmulator(): Boolean {
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\ == Build.PRODUCT
|| Build.HARDWARE.contains(\goldfish\)
|| Build.HARDWARE.contains(\ranchu\))
}

/**
* 检测是否正在被调试
* @return true表示正在调试,false表示未调试
*/
fun isDebuggerConnected(): Boolean {
return android.os.Debug.isDebuggerConnected()
}

/**
* 检测USB调试是否开启
*/
fun isUsbDebuggingEnabled(context: Context): Boolean {
return Settings.Global.getInt(
context.contentResolver,
Settings.Global.ADB_ENABLED,
0
) > 0
}

/**
* 综合安全检查
* @return 检查结果和风险提示
*/
fun performSecurityCheck(context: Context): SecurityCheckResult {
val risks = mutableListOf<String>()

if (isDeviceRooted()) {
risks.add(\设备已Root,存在安全风险\)
}

if (isEmulator()) {
risks.add(\检测到模拟器环境\)
}

if (isDebuggerConnected()) {
risks.add(\检测到调试器连接\)
}

if (isUsbDebuggingEnabled(context)) {
risks.add(\USB调试已开启\)
}

return SecurityCheckResult(
isSafe = risks.isEmpty(),
risks = risks
)
}

/**
* 安全检查结果
*/
data class SecurityCheckResult(
val isSafe: Boolean,
val risks: List<String>
)
}

5.2 在应用中使用安全检测

在 MainActivity.kt 或 Application.kt 中添加:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// 生产环境安全检查
if (!BuildConfig.DEBUG) {
val result = SecurityUtils.performSecurityCheck(this)
if (!result.isSafe) {
// 显示警告或拒绝运行
AlertDialog.Builder(this)
.setTitle(\安全警告\)
.setMessage(result.risks.joinToString(\\\n\))
.setPositiveButton(\继续使用\) { _, _ -> }
.setNegativeButton(\退出\) { _, _ -> finish() }
.show()
}
}
}

5.3 防截屏保护

在敏感页面添加防截屏:

// 在Activity的onCreate中
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
}

6. 应用加固

6.1 使用第三方加固平台

推荐使用以下加固平台:

6.2 加固流程

# 1. 编译Release版本
./gradlew assembleRelease

# 2. 上传到加固平台
# - 选择加固选项
# - 下载加固后的APK
# - 重新签名(加固会破坏签名)

# 3. 测试加固后的APK

7. 完整实施清单

7.1 需要修改的文件

  1. pp/build.gradle.kts - 配置混淆、签名、依赖
  2. pp/proguard-rules.pro - ProGuard规则
  3. pp/src/main/java/com/awen/running/network/RetrofitClient.kt - 网络安全配置
  4. pp/src/main/res/xml/network_security_config.xml - 网络安全配置
  5. pp/src/main/java/com/awen/running/security/EncryptedStorage.kt - 加密存储
  6. pp/src/main/java/com/awen/running/security/SecurityUtils.kt - 安全检测
  7. pp/src/main/AndroidManifest.xml - 网络安全配置引用

7.2 需要执行的命令

  1. 生成签名密钥:keytool -genkey -v -keystore running-release.keystore -alias running-key -keyalg RSA -keysize 2048 -validity 10000
  2. 构建Release:KEYSTORE_PASSWORD=your_password KEY_PASSWORD=your_password ./gradlew assembleRelease

7.3 验证步骤

  1. 检查混淆是否生效:反编译APK查看代码是否被混淆
  2. 验证SSL证书锁定:尝试使用Charles抓包HTTPS请求
  3. 确认数据加密:检查SharedPreferences文件内容
  4. 验证签名:检查APK签名信息

8. 安全等级评估

安全措施 状态 评分
代码混淆 已启用
SSL证书锁定 已配置
敏感数据加密 已启用
条件日志输出 已启用
安全检测 已集成
应用加固 推荐实施

综合安全等级企业级(适合商业应用)

9. 总结

通过实施以上安全措施,您的Android应用将具备:

  • 代码级别的保护(混淆和加固)
  • 网络通信的安全(SSL证书锁定)
  • 数据存储的安全(加密存储)
  • 运行时环境的安全(安全检测)
  • 发布版本的安全(签名保护)

这些措施将大大提高应用的安全性,保护用户数据和企业利益。记住,安全是一个持续的过程,需要定期评估和更新安全措施以应对新的威胁。


文档创建时间:2026年2月
版本:1.0

文章作者:阿文
文章链接: https://www.awen.me/post/5cbb38ce.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿文的博客