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();
M7:不安全的通信
这个漏洞,说白了就是App和服务器之间传数据的时候没穿防弹衣。HTTPS没配好、证书校验跳过、或者用了不安全的自定义协议,都算。
我记得有一次做渗透测试,一个社交App的登录接口用的是HTTP。我直接在Wi-Fi上开了个中间人代理,抓包一看,用户名密码全是明文。更离谱的是,App里还写死了忽略SSL证书错误——setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)。这等于把大门敞开了。
检测方法:
- 抓包测试:用Burp Suite或Charles设置代理,看App是否正常通信。如果App不报错,说明证书校验可能有问题。
- 代码审查:搜索
ALLOW_ALL_HOSTNAME_VERIFIER、setDefaultHostnameVerifier、TrustManager等关键字。 - 网络库配置检查:OkHttp的
hostnameVerifier、sslSocketFactory是否自定义过?
// 错误示例:信任所有证书
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); // 危险!
M8:不安全的认证/授权
这个漏洞,说白了就是App没搞清楚“你是谁”和“你能干什么”。比如,登录接口没做频率限制、Token过期时间太长、或者权限校验只在前端做。
我遇到过最典型的案例:一个电商App,用户A修改自己的收货地址时,请求里带了user_id=123。我随手改成user_id=456,结果成功修改了用户B的地址。这就是典型的“水平越权”——后端没校验当前登录用户和操作对象是否匹配。
检测方法:
- 越权测试:抓包修改请求中的用户ID、订单ID等参数,看能否操作其他用户的数据。
- Token测试:检查Token是否包含敏感信息(比如Base64编码的用户名),以及Token的过期时间是否合理。
- 暴力破解测试:用Burp Intruder对登录接口做字典攻击,看是否有频率限制或验证码。
M9:不安全的加密
这个漏洞,不是说你没用加密,而是你用了错误的加密方式。比如,用了已经被破解的算法(DES、MD5)、自己写的加密算法、或者密钥硬编码在代码里。
我见过最搞笑的一次:一个App用AES加密数据,密钥是"1234567890123456",而且就写在Java代码的字符串常量里。反编译一下,直接就能看到。这跟没加密有什么区别?
检测方法:
- 静态分析:搜索
SecretKeySpec、Cipher、MessageDigest等类,检查算法和密钥来源。 - 反编译检查:用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();
DES、MD5、SHA1、ECB,直接标高危。另外,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 配置不当 | 文件泄露 | 严格限制路径和权限 |
android:debuggable="true",然后上线前忘了改回去。我建议在CI/CD流程里加一个自动化检查脚本,一旦发现Release包里有debuggable=true,直接阻断构建。
知识体系总览
下面这张图,我把M6到M10的核心逻辑串起来了。你可以把它当作一个快速排查清单。
好了,M6到M10就讲到这里。这几个漏洞,说白了都是“细节决定成败”。你只要在测试时多留个心眼,多抓几次包、多翻翻配置文件,基本都能发现。记住,自动化工具能帮你省时间,但真正挖到深坑的,还得靠你手动去抠。