文章

安卓客户端安全点及案例梳理

1. 安卓客户端与去中心化应用(DApp)交互方式安全

WebView安全性控制

WebView在DApp中广泛用于加载前端网页、展示交易信息及交互界面。但是,WebView本身存在诸多安全风险,主要表现为:

审查维度

说明及防护措施

JavaScript 支持

开启 setJavaScriptEnabled(true)

是 dapp 的刚需,但增加了 XSS、代码注入等攻击面。

addJavascriptInterface

此接口若对任意网页开放,极易被 JS 恶意利用操控本地钱包/资产。只对信任的网址(白名单机制)开放。

加载 URL 风险

禁止动态拼装不可信 dapp 地址,需做严格校验或只允许内置、人工审核过的网址列表。

数据泄露

WebView 的缓存/cookie/localStorage 可能被后续加载的恶意网页读取敏感信息,需限制敏感数据缓存。

支持本地文件加载

setAllowFileAccess(true)

/setAllowFileAccessFromFileURLs(true)

有本地读写风险,慎用。

截图、屏幕录制

助记词、账户相关页面务必关闭截图与录屏:window.setFlags(WindowManager.LayoutParams.FLAG_SECURE)

资源请求控制

通过拦截目标 URL、资源加载(如 shouldInterceptRequest),防止注入第三方广告/劫持脚本、伪造合约交互等。

RPC接口调用安全性审核

  • 通信加密:所有RPC接口调用必须使用HTTPS协议进行安全传输,防止中间人攻击。配合TLS/SSL技术对数据进行传输加密具有重要意义。

  • 证书固定(Certificate Pinning):在客户端中实现证书固定,确保与正确服务器建立连接,即使DNS或网络被劫持也能有效防范。

  • 输入验证:所有通过RPC接口传输的数据,必须在接收端和发送端同时进行严格的输入验证和数据校验,以防止SQL注入、跨站脚本攻击(XSS)及数据篡改。

通过安全审核的RPC调用,不仅要求加密传输,还应在协议层面上确保数据的完整性和不可篡改性。下图展示了RPC接口调用的安全数据传输流程:

钱包连接协议安全性措施

现代DApp通常依赖钱包应用(如MetaMask、Trust Wallet等)来实现去中心化的身份认证和交易签名。通过诸如WalletConnect的协议,安卓客户端可以与钱包应用建立安全连接,但这中间也存在风险。

主要风险点

  • 钱包劫持:在启动钱包连接时,若使用隐式Intent,存在恶意应用冒充目标钱包接管连接的风险。

  • 会话验证:建立连接前,必须通过显式Intent或验证接收方的包名,确保仅与受信任的钱包应用通信。

  • 交易数据完整性:在交易请求展示给用户前,若数据在传输或显示阶段被修改,则可能造成用户确认错误交易数据,从而发生签名错误。

防护措施

  • 使用显式Intent启动钱包应用,或在连接过程中严格验证对方身份。

  • 确保连接初始化时的握手信息包含版本号、通信用的Topic以及用于加密签名的密钥,防止重放攻击和中间人篡改。

  • 在交易签名前,将数据在安全环境中展示,确保用户确认信息真实、未被篡改。

WalletConnect 审查点

审查维度

关键审计点

连接建立

- 元数据验证: 在建立连接时,是否向用户清晰展示请求连接的dApp的名称、URL、图标等元数据?应用是否验证了这些信息的真实性?
-
深链接(Deep Link)漏洞: 审查处理WalletConnect深链接的Activity,确保其对传入的URI参数进行了严格的验证,防止恶意参数注入。

会话管理

- 会话信息存储: WalletConnect的会话密钥(Session Key)是否被安全地存储?
-
用户断连: 应用是否提供了清晰易用的界面,让用户可以随时查看当前活动的连接,并主动断开任何一个dApp的连接?

签名请求

- 请求来源验证: 收到的签名请求是否与当前已建立连接的dApp会话匹配?防止其他应用伪造签名请求。
-
遵循交易展示原则: 所有通过WalletConnect发起的交易请求,都必须遵循上一章节“交易构建与展示”中的所有安全原则。

2. 安卓客户端数据存储安全审计

数据存储安全是安卓客户端安全防御体系中的重要一环。由于DApp应用中往往涉及私钥、助记词、交易记录等敏感信息,如何妥善存储这些数据直接关系到用户资产安全。

内部存储加密与权限隔离

安卓系统为应用提供了内部存储,该存储方式天生受系统沙箱机制保护,只有应用自身才能访问。但若将敏感数据以明文形式存储在内部存储中,仍然存在被恶意软件或Root设备绕过安全防护后读取的风险。

安全措施

  • 数据加密存储:应使用诸如AES-256的先进加密算法对敏感数据进行加密,再存储到内部存储中。Android Developers建议利用 EncryptedSharedPreferencesEncryptedFile 来管理重要数据。

  • 权限模式设置:确保文件的读写权限使用 MODE_PRIVATE,防止被其他应用访问。

  • 数据清理策略:定期清理无用数据,减少潜在泄露风险。

Android Keystore及硬件安全模块

Android Keystore提供了一种专用的机制,用于存储敏感加密密钥,其核心优势包括利用硬件支持(如TEE或SE)隔离密钥,不允许操作系统以外的程序访问。在涉及DApp的私钥管理时,利用Keystore不仅能防止密钥被硬编码,还能有效避免在设备被攻破后直接从文件系统中提取密钥。

最佳实践

  • 硬件支持验证:通过 isInsideSecureHardware 方法验证密钥是否存储在硬件中。

  • 密钥使用策略:限制密钥的使用场景,设定授权操作,防止密钥滥用。

  • 定期密钥轮换:为了防范长期密钥泄露风险,应定期更新密钥和相关密码。

日志管理与敏感数据防泄露

日志文件中若记录了敏感信息(如用户私钥、会话令牌等),可能成为攻击者突破防线的重要途径。OWASP文档建议开发者在记录日志时务必过滤和脱敏,避免将敏感信息写入到可被外部访问的存储区域。

防护建议

  • 敏感数据过滤:所有日志记录操作必须排除敏感数据,包括账号、密码、私钥等。

  • 加密日志传输:当日志需要上传到服务器进行分析时,应确保日志内容经过加密处理,防止在传输过程中被窃取。

  • 权限控制:使用内部存储保存日志,并确保日志文件权限设定为仅当前应用可见。

存储方式

基本属性

安全防护措施

内部存储

受沙箱隔离,无需额外权限

使用 MODE_PRIVATE、数据加密、定期清理

加密SharedPreferences

存储小型键值对数据,可加密

利用 EncryptedSharedPreferences 自动加密数据

外部存储

易被其他应用访问

尽量不存储敏感数据,必须对敏感信息进行加密,并严格管理读写权限

Android Keystore

专用密钥存储,支持硬件加固

验证硬件安全,限制密钥使用,并定期进行密钥轮换

表格 2:数据存储方式及其安全防护措施比较

3. 安卓权限管理与代码安全防护

在安卓平台上,权限管理和代码安全防护是确保客户端安全的基础环节。众所周知,权限请求过多或权限配置不当,都会使应用面临攻击风险,而动态代码加载和代码混淆等问题也会引发逆向工程风险,使攻击者从中窃取敏感数据或利用漏洞进行攻击。

最小化权限请求与签名权限策略

所有安全审计的基本原则之一是遵循最小权限原则。安卓应用应当仅请求必要的权限,否则将为恶意攻击者提供更多攻击面。

主要措施

  • 最小权限原则:只请求应用运行所必须的权限,必要时动态请求而非在安装时全部申请。

  • 签名权限使用:在多个应用间共享数据时,优先使用签名级权限,确保只有由同一开发者签名的应用才能访问共享数据。

  • 组件导出管理:对于不希望外部应用调用的组件(Activity、Service、BroadcastReceiver等),必须在AndroidManifest.xml中设置android:exported="false"

组件导出控制与Intent安全审计

恶意应用可能通过注册相同的Intent Filter来模仿目标应用,从而捕获敏感数据或窃取交易信息。对此,开发者必须在设计应用时采取措施:

  • 使用显式Intent:在启动钱包或其他敏感组件时,使用明确包名的Intent,避免隐式Intent带来的风险。

  • 验证Intent签名:在接受外部数据时,必须校验发送方的数字签名或包名,确保其合法性。

代码混淆、动态加载与静态检测方法

防止应用被逆向工程和代码篡改是确保安全的重要措施。

  • 代码混淆与加固:利用ProGuard/R8工具进行代码混淆,可有效降低逆向工程的风险。

  • 避免动态加载:尽量避免从外部加载代码,防止在运行时意外执行未经验证的代码。

  • 静态与动态安全测试:定期采用SAST(静态应用安全测试)和DAST(动态应用安全测试)工具,检测代码中的安全漏洞。

流程:代码编写 --> 代码混淆加固-->静态代码扫描-->动态测试-->性能评估与漏洞修复

4. 密钥管理与数据传输安全

密钥管理和数据传输安全是DApp交互中尤为关键的环节。私钥、助记词作为身份认证核心资料,其安全性直接关系到用户资产的安全。同时,数据在客户端与服务器或区块链网络之间的传输过程,若处理不当,也会引发中间人攻击等安全风险。

私钥存储与密钥生命周期管理

私钥及助记词的管理需要采取严密措施:

  • 硬编码风险防范:绝对不允许在代码中硬编码私钥或助记词;应通过安全的输入设备或硬件安全模块获取。

  • 加密存储:利用Android Keystore或EncryptedSharedPreferences强加密存储敏感数据,并确保密钥管理策略符合业务需求。

  • 密钥轮换机制:定期更换私钥、令牌和API密钥,防止长期使用导致泄露风险。

审查维度

关键审计点

私钥/助记词生成

- 熵源随机性: 是否使用加密安全的伪随机数生成器 (CSPRNG) 来生成熵?应避免使用java.util.Random等可预测的随机源。

- 离线生成: 敏感的密钥生成过程是否在设备离线状态下完成,以防止网络嗅探?

私钥/助记词存储

- 严禁明文存储: 绝不允许将私钥、助记词或Keystore密码以明文形式存储在SharedPreferences、数据库、或应用私有目录的普通文件中。
-
硬件级加密: 是否优先使用安卓的硬件支持密钥库(Hardware-backed Keystore)?通过KeyGenParameterSpec.Builder.setIsStrongBoxBacked(true)可以强制使用StrongBox,提供最高级别的安全防护。

- Android Keystore System: 如果未使用硬件级加密,是否正确使用Android Keystore System来加密密钥?检查密钥的生成参数,如是否设置了用户认证要求 (setUserAuthenticationRequired(true))。

- 文件加密: Keystore文件或加密后的私钥是否存储在应用的内部存储空间 (/data/data/<package_name>),并避免使用外部存储?

私钥使用(签名)

- 内存安全: 私钥在内存中的停留时间是否最小化?在使用后是否立即从内存中清除?
-
用户明确授权: 每一笔交易签名是否都需要用户通过生物识别(指纹、面容)或PIN码进行授权?检查KeyGenParameterSpec.Builder.setUserAuthenticationValidityDurationSeconds()的设置,确保不是一次授权长期有效。

- 签名隔离: 签名操作是否在独立的、受控的环境中执行,防止应用的其他部分非预期地访问到签名逻辑?

备份与恢复

- 安全提示: 应用是否以清晰、明确且强制性的方式,引导用户备份助记词或私钥,并强调离线存储的重要性?
-
导入安全: 在导入私钥或助记词时,是否采取了防截图、防录屏措施?输入内容是否在内存中得到妥善处理?

TLS/SSL加密通信与证书固定

在数据传输过程中,必须保障信息保密性和完整性。主要措施包括:

  • TLS/SSL加密:所有客户端与服务器之间的通信必须使用HTTPS,确保数据在传输时被加密。

  • 证书固定:在客户端应用中实现证书固定机制,防止DNS劫持、网络中间人攻击,从而确保服务器身份的真实性。

防止交易数据篡改和重放攻击

在与DApp交互时,交易数据在签名确认之前易受篡改风险,因此应采取如下措施:

  • 交易数据完整性验证:在用户签名前,利用哈希算法检验交易数据的完整性,确保数据未被非法修改。

  • 重放攻击防范:使用会话令牌、时间戳等机制,确保每笔交易的唯一性,避免重放攻击。

  • 安全展示交易详情:在签名交易数据之前,将关键信息在安全环境中展示给用户审核,防止恶意应用通过内存篡改数据。

交易数据签名安全传输流程

- 交易数据生成;

- 数据哈希及完整性校验;

- 安全传输(TLS/SSL加密);

- 交易详情安全展示;

- 用户数字签名;

- 防止重放机制验证。

5. 用户认证,授权与交易构建安全机制

用户认证是保证应用安全的另一个核心环节,特别是在涉及敏感数据和交易操作的DApp场景中,强认证机制能够有效防止未授权访问及会话劫持。

多因素认证与生物识别安全

近年来,基于生物识别技术(如指纹识别、面部识别)和多因素认证(MFA)已成为提高用户认证安全性的重要手段:

  • 多因素验证:结合密码、生物识别和OTP(一次性密码)等方法,显著降低攻击者的入侵成功率。

  • 生物识别安全:通过硬件级安全模块对生物识别数据进行存储和处理,防止数据在传输或存储过程中泄密。

  • 分布式身份验证:部分DApp采用去中心化身份认证机制,通过公钥密码学提高身份认证的安全性。

OAuth2与基于角色的访问控制

在涉及敏感数据访问和交易授权的应用中,必须引入稳定成熟的授权框架来规范访问控制:

  • OAuth2标准:使用OAuth2进行安全授权与令牌交换,确保客户端与服务端之间的授权信息经过加密传递。

  • 基于角色的访问控制(RBAC):对操作权限进行精细化管理,根据用户角色分配最小权限,从而降低意外风险。

  • 会话管理与超时策略:实施严格的会话管理,及时销毁闲置会话,避免会话劫持。

下表总结了多因素认证和OAuth2授权机制的主要特点及优势:

认证机制

主要特点

优势描述

多因素认证 (MFA)

结合密码、生物识别和OTP

显著降低攻击成功率,增强用户账户安全

生物识别认证

利用指纹、面部识别技术

提高使用便捷性和安全性,防止密码泄露

OAuth2授权

基于令牌的安全授权

支持分布式系统中安全令牌管理,确保服务器与客户端安全通信

基于角色访问控制(RBAC)

按用户角色分配权限

实现最小权限原则,降低操作误差风险

用户认证与授权安全措施比较

交易构建审查

审查维度

关键审计点

交易数据解析

- 数据源可信度: 用于构建交易的数据(如目标地址、合约地址、金额、calldata)来源是否可靠?是否对来自dApp的数据进行了严格的校验?

- calldata解析: 是否能解析常见的智能合约方法调用(如ERC20的transferapprove),并以人类可读的格式展示给用户?对于无法解析的复杂交易,是否向用户明确提示风险?

用户界面 (UI/UX)

- 关键信息醒目: 交易确认界面是否清晰、无歧义地展示了所有关键信息?包括:转账对手方地址、转账金额、Gas费用、调用的合约地址及方法。
- 地址校验: 是否对地址进行了校验和(Checksum)检查?是否提供了方便用户核对地址的机制(如部分隐藏、点击复制)?
- “盲签”风险提示: 如果交易数据(特别是calldata)过于复杂无法完全解析,是否会向用户发出“盲签”(Blind Signing)的严重警告?

Gas费用估算

- 费用估算逻辑: Gas价格和Gas Limit的估算逻辑是否合理?是否允许用户在理解风险的前提下自定义高级选项?
- 防止巨额Gas: 是否有机制防止dApp恶意设置极高的Gas费用?

6. 实际案例分析与风险应对实例

WebView漏洞案例及修复策略

案例描述:
某DApp安卓客户端通过WebView加载外部交易页面,未正确禁用文件访问和JavaScript启用。攻击者构造恶意URI,利用不当的
setAllowFileAccessFromFileURLs配置,成功访问了应用私有目录中的敏感配置文件(例如SharedPreferences中存储的密钥文件),导致用户密钥泄露。

修复措施:

  • 禁用 setAllowFileAccessFromFileURLssetAllowUniversalAccessFromFileURLs

  • 改用 WebViewAssetLoader 上传本地资源,确保仅加载可信资源;

  • 限制JavaScript启用,仅在必要场景下,并严格验证加载内容。

密钥存储不当实例及防范措施

案例描述:
某DApp客户端错误地将助记词以明文形式存储在SharedPreferences中。当设备被Root后,攻击者利用文件读取工具轻松提取了助记词,从而获得用户资产控制权。

修复措施:

  • 使用“EncryptedSharedPreferences”或Android Keystore存储敏感数据;

  • 避免在代码中硬编码任何私钥或助记词;

  • 定期审查和轮换关键敏感数据,确保及时更新12

钱包连接劫持的预防与应急措施

案例描述:
在钱包连接过程中,利用隐式Intent启动钱包应用,使得恶意应用冒充目标钱包接受链接请求,从而窃取用户的签名请求信息。

修复措施:

  • 使用显式Intent,并在发起连接时验证目标钱包应用的包名;

  • 在握手过程中验证协议版本、通信Topic及加密密钥,确保连接唯一合法。

  • 针对连接建立期间加入状态监控和日志审计,便于事后审计和追踪。



许可协议:  CC BY 4.0