8、OWASP Mobile Top 10:M6-M10漏洞详解与检测方法

好,咱们继续往下聊。前面五个漏洞(M1-M5)算是移动应用里最常踩的坑,但M6到M10这几个,说实话,更容易被忽视。我见过不少团队,前五个漏洞修得干干净净,结果在M6上翻了车。为什么?因为这几个问题往往藏在业务逻辑和代码细节里,自动化工具扫不出来,全靠人工抠。

今天我就把这五个漏洞掰开揉碎了讲。每个我都会结合自己的踩坑经历,告诉你到底怎么测、怎么修。

M6:不安全的数据存储

说白了,就是敏感数据没存对地方。你想想看,用户密码、Token、身份证号,这些玩意儿要是明文躺在SharedPreferences里,跟把钥匙挂在门上有什么区别?

我在项目中遇到过一件事:一个金融类App,开发图省事,把用户登录后的Session Token直接写进了外部存储的文本文件里。结果呢?任何有文件读写权限的应用都能把它读走。攻击者只要装个恶意App,就能悄无声息地拿到Token,然后冒充用户操作。

常见高危场景:
  • SharedPreferences存明文密码或Token
  • SQLite数据库未加密
  • 日志中打印敏感信息(Log.d、Log.e)
  • WebView缓存包含登录态数据
  • Keychain/Keystore使用不当(比如存了但没设置访问限制)

检测方法:

  • 静态分析:用MobSF或QARK扫描,重点关注SharedPreferences、SQLiteOpenHelper、FileOutputStream等API调用。
  • 动态分析:Root后的设备上,用adb shell直接查看/data/data/包名/下的文件。我习惯用adb shell run-as 包名 cat /data/data/包名/shared_prefs/*.xml
  • 日志检查:用adb logcat | grep -i password过滤,看看有没有敏感信息泄露。
// 错误示例:明文存储
SharedPreferences.Editor editor = getSharedPreferences("user", MODE_PRIVATE).edit();
editor.putString("password", "123456"); // 危险!
editor.apply();

// 正确做法:使用EncryptedSharedPreferences
MasterKey masterKey = new MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build();
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secure_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
sharedPreferences.edit().putString("password", encryptedValue).apply();
我的建议: 别信开发说的“我们用了加密”。你得亲自验证——把App跑起来,用文件管理器把数据库拷出来,用SQLite Browser打开看看。我曾经就发现过“加密”只是个Base64编码,一解码就原形毕露。

M7:不安全的通信

这个漏洞,说白了就是App和服务器之间传数据的时候没穿防弹衣。HTTPS没配好、证书校验跳过、或者用了不安全的自定义协议,都算。

我记得有一次做渗透测试,一个社交App的登录接口用的是HTTP。我直接在Wi-Fi上开了个中间人代理,抓包一看,用户名密码全是明文。更离谱的是,App里还写死了忽略SSL证书错误——setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)。这等于把大门敞开了。

检测方法:

  • 抓包测试:用Burp Suite或Charles设置代理,看App是否正常通信。如果App不报错,说明证书校验可能有问题。
  • 代码审查:搜索ALLOW_ALL_HOSTNAME_VERIFIERsetDefaultHostnameVerifierTrustManager等关键字。
  • 网络库配置检查:OkHttp的hostnameVerifiersslSocketFactory是否自定义过?
// 错误示例:信任所有证书
TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType) {}
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
    }
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); // 危险!
注意: 有些App只在Release版本里才启用证书校验,Debug版本是跳过的。所以测试时一定要用Release包,或者检查BuildConfig.DEBUG的开关逻辑。

M8:不安全的认证/授权

这个漏洞,说白了就是App没搞清楚“你是谁”和“你能干什么”。比如,登录接口没做频率限制、Token过期时间太长、或者权限校验只在前端做。

我遇到过最典型的案例:一个电商App,用户A修改自己的收货地址时,请求里带了user_id=123。我随手改成user_id=456,结果成功修改了用户B的地址。这就是典型的“水平越权”——后端没校验当前登录用户和操作对象是否匹配。

检测方法:

  • 越权测试:抓包修改请求中的用户ID、订单ID等参数,看能否操作其他用户的数据。
  • Token测试:检查Token是否包含敏感信息(比如Base64编码的用户名),以及Token的过期时间是否合理。
  • 暴力破解测试:用Burp Intruder对登录接口做字典攻击,看是否有频率限制或验证码。
避坑指南: 我曾经测过一个App,它的Token有效期是30天。我拿到一个Token后,过了两周还能正常使用。这要是用户手机丢了,攻击者能白嫖半个月。建议Token有效期不超过24小时,并且要有刷新机制。

M9:不安全的加密

这个漏洞,不是说你没用加密,而是你用了错误的加密方式。比如,用了已经被破解的算法(DES、MD5)、自己写的加密算法、或者密钥硬编码在代码里。

我见过最搞笑的一次:一个App用AES加密数据,密钥是"1234567890123456",而且就写在Java代码的字符串常量里。反编译一下,直接就能看到。这跟没加密有什么区别?

检测方法:

  • 静态分析:搜索SecretKeySpecCipherMessageDigest等类,检查算法和密钥来源。
  • 反编译检查:用jadx或JEB反编译APK,搜索硬编码的字符串,看有没有像密钥的东西。
  • 算法强度评估:检查是否使用了ECB模式(不应该用)、是否使用了不安全的随机数生成器(Random而不是SecureRandom)。
// 错误示例:硬编码密钥 + ECB模式
byte[] key = "FixedKey12345678".getBytes(); // 硬编码!
SecretKeySpec spec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // ECB模式不安全!

// 正确做法:使用KeyStore生成密钥,使用GCM模式
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(new KeyGenParameterSpec.Builder("my_key",
    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
    .build());
SecretKey secretKey = keyGenerator.generateKey();
我的习惯: 测试加密强度时,我会先看算法名称。如果看到DESMD5SHA1ECB,直接标高危。另外,PBEWithMD5AndDES这种组合,基本等于没加密。

M10:不安全的配置

这个漏洞,范围比较广。说白了,就是App的配置文件、权限声明、或者运行时设置出了问题。比如,AndroidManifest.xml里开了android:allowBackup="true"android:debuggable="true",或者导出了不该导出的Activity。

我遇到过最坑的一个:一个银行App,它的AndroidManifest.xml里声明了一个WebViewActivity,并且exported="true"。攻击者可以直接通过Intent启动这个Activity,加载一个恶意的HTML页面,然后利用WebView的JavaScript接口执行任意代码。

检测方法:

  • Manifest分析:用aapt dump badging app.apk或MobSF查看权限和组件导出情况。
  • 备份测试:用adb backup -f backup.ab -noapk 包名尝试备份,看是否成功。
  • 调试测试:用adb shell dumpsys package 包名查看DEBUGGABLE标志。
常见高危配置:
配置项风险建议
android:allowBackup="true"攻击者可通过adb备份窃取数据设为false
android:debuggable="true"Release版本可被调试设为false,或通过BuildConfig控制
Activity exported="true"外部应用可启动该Activity如非必要,设为false
WebView 启用JavaScript存在XSS风险仅加载可信内容时启用
FileProvider 配置不当文件泄露严格限制路径和权限
警告: 很多开发为了方便调试,会在AndroidManifest里加上android:debuggable="true",然后上线前忘了改回去。我建议在CI/CD流程里加一个自动化检查脚本,一旦发现Release包里有debuggable=true,直接阻断构建。

知识体系总览

下面这张图,我把M6到M10的核心逻辑串起来了。你可以把它当作一个快速排查清单。

M6-M10 漏洞检测知识体系 M6 不安全的数据存储 SharedPreferences / SQLite / 日志 M7 不安全的通信 HTTPS / 证书校验 / 中间人攻击 M8 不安全的认证/授权 越权 / Token / 暴力破解 M9 不安全的加密 弱算法 / 硬编码密钥 / ECB模式 M10 不安全的配置 Manifest / 备份 / 调试 / 导出组件 检测方法汇总 静态分析 / 动态分析 / 抓包 核心检测思路 1. 静态分析:反编译APK,检查代码和配置文件 2. 动态分析:运行App,抓包、查看文件、调试 3. 工具辅助:MobSF / QARK / Burp Suite / Frida

好了,M6到M10就讲到这里。这几个漏洞,说白了都是“细节决定成败”。你只要在测试时多留个心眼,多抓几次包、多翻翻配置文件,基本都能发现。记住,自动化工具能帮你省时间,但真正挖到深坑的,还得靠你手动去抠。

公众号:蓝海资料掘金营,微信deep3321