移动应用密码学¶
密码学在保护用户数据方面发挥着尤为重要的作用——在移动环境中更是如此,因为攻击者很有可能物理访问用户的设备。本章概述了与移动应用相关的密码学概念和最佳实践。这些最佳实践独立于移动操作系统。
关键概念¶
密码学的目标是提供持续的机密性、数据完整性和真实性,即使面对攻击。机密性涉及通过加密确保数据隐私。数据完整性通过使用哈希处理来处理数据一致性以及篡改和修改数据的检测。真实性确保数据来自可信来源。
加密算法将明文数据转换为密文,从而隐藏原始内容。明文数据可以通过解密从密文中恢复。加密有两种类型:对称加密(加密和解密使用相同的密钥)和非对称加密(加密和解密使用公钥和私钥对)。除非与支持带随机初始化向量(IV)的认证加密的批准密码模式一起使用,否则对称加密操作不保护数据完整性,该IV满足“唯一性”要求 NIST SP 800-38D - “分组密码操作模式建议:伽罗瓦/计数器模式(GCM)和GMAC”,2007。
对称密钥加密算法对加密和解密使用相同的密钥。这种类型的加密速度快,适用于批量数据处理。由于所有能访问密钥的人都能解密加密内容,因此这种方法需要谨慎的密钥管理和对密钥分发的集中控制。
公钥加密算法使用两个独立的密钥进行操作:公钥和私钥。公钥可以自由分发,而私钥不应与任何人共享。使用公钥加密的消息只能使用私钥解密,反之亦然。由于非对称加密比对称操作慢几倍,它通常只用于加密少量数据,例如用于批量加密的对称密钥。
哈希处理不是一种加密形式,但它确实使用密码学。哈希函数以确定性方式将任意数据映射为固定长度的值。虽然从输入计算哈希很容易,但从哈希确定原始输入非常困难(即不可行)。此外,即使输入的一个比特发生变化,哈希也会完全改变。哈希函数用于存储密码、验证完整性(例如数字签名或文档管理)以及管理文件。尽管哈希函数不提供真实性保证,但它们可以作为密码学原语组合以实现真实性。
消息认证码(MAC)将其他密码机制(如对称加密或哈希)与秘密密钥结合,以提供完整性和真实性保护。然而,为了验证MAC,多个实体必须共享相同的秘密密钥,并且任何这些实体都可以生成有效的MAC。HMAC是MAC最常用的类型,它依赖哈希作为底层密码学原语。HMAC算法的全名通常包含底层哈希函数的类型(例如,HMAC-SHA256使用SHA-256哈希函数)。
签名将非对称密码学(即使用公钥/私钥对)与哈希结合,通过使用私钥加密消息的哈希值来提供完整性和真实性。然而,与MAC不同,签名还提供不可否认性,因为私钥应保持对数据签名者的唯一性。
密钥派生函数(KDF)从秘密值(如密码)派生秘密密钥,并用于将密钥转换为其他格式或增加其长度。KDF与哈希函数类似,但也具有其他用途(例如,它们被用作多方密钥协商协议的组件)。虽然哈希函数和KDF都必须难以逆向,但KDF有额外的要求,即它们生成的密钥必须具有一定程度的随机性。
识别不安全和/或已废弃的密码算法¶
评估移动应用程序时,应确保其不使用具有重大已知弱点或不足以满足现代安全要求的密码算法和协议。过去被认为是安全的算法可能会随着时间变得不安全;因此,定期检查当前的最佳实践并相应调整配置非常重要。
验证密码算法是最新的,并符合行业标准。易受攻击的算法包括过时的分组密码(如DES和3DES)、流密码(如RC4)、哈希函数(如MD5和SHA1)以及损坏的随机数生成器(如Dual_EC_DRBG和SHA1PRNG)。请注意,即使是经过认证的算法(例如,NIST认证的)也可能随着时间变得不安全。认证不能代替对算法健全性的定期验证。具有已知弱点的算法应替换为更安全的替代方案。此外,用于加密的算法必须标准化并开放验证。使用任何未知或专有算法加密数据可能会使应用程序面临不同的密码攻击,从而可能导致明文恢复。
检查应用程序的源代码,以识别已知弱密码算法的实例,例如
密码学API的名称取决于特定的移动平台。
请确保
- 密码算法是最新的,并符合行业标准。这包括但不限于过时的分组密码(例如DES)、流密码(例如RC4),以及哈希函数(例如MD5)和损坏的随机数生成器(例如Dual_EC_DRBG,即使它们经过NIST认证)。所有这些都应标记为不安全,不应使用并从应用程序和服务器中删除。
- 密钥长度符合行业标准,并能在长时间内提供足够的保护。不同密钥长度及其提供的保护(考虑摩尔定律)的比较可在在线找到。
- 通过 NIST SP 800-131A - “加密算法和密钥长度的使用转换”,2024,NIST提供了关于与未来建议保持一致和过渡到更强加密密钥和更稳健算法的建议和指导。
- 密码学手段不相互混淆:例如,不要用公钥签名,也不要尝试重用用于签名的密钥对进行加密。
- 密码学参数在合理范围内定义良好。这包括但不限于:密码盐,其长度应至少与哈希函数输出相同;合理选择密码派生函数和迭代次数(例如PBKDF2、scrypt或bcrypt);IVs是随机且唯一的;适合用途的分组加密模式(例如,ECB不应使用,除非有特殊情况);密钥管理做得妥当(例如,3DES应有三个独立的密钥),等等。
推荐算法
- 机密性算法:AES-GCM-256 或 ChaCha20-Poly1305
- 完整性算法:SHA-256、SHA-384、SHA-512、BLAKE3、SHA-3 系列
- 数字签名算法:RSA(3072 位及更高)、带 NIST P-384 的 ECDSA 或带 Edwards448 的 EdDSA。
- 密钥建立算法:RSA(3072 位及更高)、DH(3072 位或更高)、带 NIST P-384 的 ECDH
请注意:这些建议基于当前行业认为合适的认知。它们与 NIST 2030 年之后的建议保持一致,但不一定考虑量子计算的进步。有关后量子密码学的建议,请参见下文的“后量子”部分。
此外,您应始终依赖安全硬件(如果可用)来存储加密密钥、执行加密操作等。
有关算法选择和最佳实践的更多信息,请参阅以下资源
- “商业国家安全算法套件和量子计算常见问题”
- NIST 建议 (2019)
- BSI 建议 (2019)
- NIST SP 800-56B 修订版 2 - “使用整数分解密码学进行成对密钥建立的建议”,2019:NIST 建议使用基于 RSA 的密钥传输方案,最小模数长度至少为 2048 位。
- NIST SP 800-56A 修订版 3 - “使用离散对数密码学进行成对密钥建立方案的建议”,2018:NIST 建议使用基于 ECC 的密钥协商方案,例如椭圆曲线 Diffie-Hellman (ECDH),使用 P-224 到 P-521 的曲线。
- FIPS 186-5 - “数字签名标准 (DSS)”,2023:NIST 批准 RSA、ECDSA 和 EdDSA 用于数字签名生成。DSA 仅应用于验证先前生成的签名。
- NIST SP 800-186 - “基于离散对数密码学的建议:椭圆曲线域参数”,2023:提供了用于基于离散对数密码学的椭圆曲线域参数的建议。
后量子¶
公钥加密算法¶
2024年,NIST批准CRYSTALS-Kyber作为一种后量子密钥封装机制(KEM),用于通过公共信道建立共享秘密。该共享秘密可随后与对称密钥算法一起用于加密和解密。
- FIPS 203 - “基于模块格的密钥封装机制标准”,2024:将 CRYSTALS-Kyber 指定为后量子密钥封装的标准。
签名¶
2024年,NIST批准SLH-DSA和ML-DSA作为后量子签名生成和验证的推荐数字签名算法。
- FIPS 205 - “无状态基于哈希的数字签名标准”,2024:指定SLH-DSA用于后量子数字签名。
- FIPS 204 - “基于模块格的数字签名标准”,2024:指定ML-DSA用于后量子数字签名。
常见配置问题¶
密钥长度不足¶
即使是最安全的加密算法,当其使用的密钥大小不足时,也会容易受到暴力攻击。
确保密钥长度符合公认的行业标准。
使用硬编码加密密钥的对称加密¶
对称加密和带密钥哈希(MAC)的安全性取决于密钥的保密性。如果密钥泄露,加密带来的安全性就会丧失。为了防止这种情况,切勿将秘密密钥与它们帮助创建的加密数据存储在同一位置。一个常见的错误是使用静态的硬编码加密密钥加密本地存储的数据,并将该密钥编译到应用程序中。这使得任何能够使用反汇编器的人都可以访问该密钥。
硬编码加密密钥意味着密钥是
- 应用程序资源的一部分
- 可以从已知值派生出的值
- 硬编码在代码中
首先,确保源代码中不存储任何密钥或密码。这意味着您应该检查 Android 上的原生代码、JavaScript/Dart 代码、Java/Kotlin 代码以及 iOS 上的 Objective-C/Swift。请注意,即使源代码经过混淆,硬编码密钥也存在问题,因为混淆很容易通过动态插桩绕过。
如果应用程序使用双向TLS(服务器和客户端证书都经过验证),请确保
- 客户端证书的密码未本地存储或锁定在设备钥匙串中。
- 客户端证书未在所有安装之间共享。
如果应用程序依赖于存储在应用程序数据中的附加加密容器,请检查加密密钥的使用方式。如果使用密钥包装方案,请确保为每个用户初始化主秘密,或者使用新密钥重新加密容器。如果可以使用主秘密或以前的密码解密容器,请检查密码更改的处理方式。
在移动应用程序中使用对称密码学时,秘密密钥必须存储在安全的设备存储中。有关平台特定API的更多信息,请参阅“Android 上的数据存储”和“iOS 上的数据存储”章节。
不当的密钥派生函数¶
密码算法(如对称加密或某些MAC)需要给定大小的秘密输入。例如,AES使用一个精确的16字节密钥。原生实现可能会直接使用用户提供的密码作为输入密钥。使用用户提供的密码作为输入密钥存在以下问题
- 如果密码小于密钥,则不使用完整的密钥空间。剩余空间被填充(有时使用空格进行填充)。
- 用户提供的密码实际上将主要由可显示和可发音的字符组成。因此,仅使用了可能的256个ASCII字符中的一部分,熵大约减少了四倍。
确保密码未直接传递给加密函数。相反,用户提供的密码应传递给KDF以创建加密密钥。使用密码派生函数时选择适当的迭代次数。例如,NIST 建议 PBKDF2 的迭代次数至少为 10,000,对于用户感知性能不关键的关键密钥,至少为 10,000,000。对于关键密钥,建议考虑实现 密码哈希竞赛 (PHC) 认可的算法,例如 Argon2。
不当的随机数生成¶
移动应用程序中的一个常见弱点是随机数生成器的不当使用。普通的伪随机数生成器 (PRNG) 虽然足以满足一般用途,但并非为密码学目的而设计。当用于生成密钥、令牌或其他安全关键值时,它们可能使系统容易受到预测和攻击。
根本问题在于确定性设备无法产生真正的随机性。PRNG 使用算法模拟随机性,但如果没有足够的熵和算法强度,输出可能变得可预测。例如,UUID 可能看起来是随机的,但不足以提供安全使用所需的熵。
正确的方法是使用密码学安全伪随机数生成器 (CSPRNG)。CSPRNG 旨在抵抗统计分析和预测,使其适用于生成不可猜测的值。所有安全敏感值都必须使用至少 128 位熵的 CSPRNG 生成。
不当的哈希处理¶
为特定目的使用错误的哈希函数可能会损害安全性和数据完整性。每个哈希函数的设计都考虑了特定的用例,不正确地应用它会引入风险。
对于完整性检查,请选择提供强大碰撞抵抗能力的哈希函数。SHA-256、SHA-384、SHA-512、BLAKE3 和 SHA-3 家族等算法适用于验证数据完整性和真实性。避免使用 MD5 或 SHA-1 等已损坏的算法,因为它们容易受到碰撞攻击。
不要将 SHA-2 或 SHA-3 等通用哈希函数用于密码哈希或密钥派生,特别是对于可预测的输入。
自定义密码学实现¶
发明专有密码函数既耗时又困难,而且很可能失败。相反,我们可以使用广为人知且被普遍认为是安全的算法。移动操作系统提供了实现这些算法的标准密码学API。
仔细检查源代码中使用的所有加密方法,特别是那些直接应用于敏感数据的方法。所有加密操作都应使用Android和iOS的标准加密API(我们将在平台特定章节中更详细地介绍)。任何不调用已知提供商的标准例程的加密操作都应仔细检查。特别注意已被修改的标准算法。请记住,编码与加密不同!当您发现位操作符(如XOR(异或))时,务必进一步调查。
在所有密码学实现中,您需要确保始终执行以下操作
- 工作密钥(如AES/DES/Rijndael中的中间/派生密钥)在消耗后或发生错误时,应从内存中正确清除。
- 密码的内部状态应尽快从内存中清除。
不当的加密¶
高级加密标准 (AES) 是移动应用中广泛接受的对称加密标准。它是一种迭代分组密码,基于一系列链接的数学操作。AES 对输入执行可变数量的轮次,每一轮都涉及输入块中字节的替换和置换。每轮都使用从原始 AES 密钥派生的 128 位轮密钥。
截至本文撰写之时,尚未发现针对AES的有效密码分析攻击。然而,实现细节和可配置参数(如分组密码模式)仍存在一些出错空间。
损坏的分组密码模式¶
基于块的加密是对离散输入块执行的(例如,AES有128位块)。如果明文大于块大小,明文会在内部被分成给定输入大小的块,并对每个块执行加密。分组密码操作模式(或块模式)决定了加密前一个块的结果是否影响后续块。
避免使用ECB(电子密码本)模式。ECB将输入分成固定大小的块,并使用相同的密钥单独加密。如果多个分块包含相同的明文,它们将被加密成相同的密文块,这使得数据中的模式更容易被识别。在某些情况下,攻击者还可能重放加密数据。
对于新设计,首选带有关联数据(AEAD)的认证加密模式,例如伽罗瓦/计数器模式(GCM)或带CBC-MAC的计数器模式(CCM),因为它们同时提供机密性和完整性。如果GCM或CCM不可用,密码块链(CBC)模式优于ECB,但应与HMAC结合使用,和/或确保不出现“填充错误”、“MAC错误”或“解密失败”等错误,以更有效地抵御填充预言攻击。在CBC模式下,明文块与前一个密文块进行异或运算,确保即使块包含相同信息,每个加密块也是唯一且随机的。
存储加密数据时,我们建议使用也能保护存储数据完整性的分组模式,例如伽罗瓦/计数器模式(GCM)。后者还有一个额外的好处是该算法是每个TLSv1.2实现的强制要求,因此在所有现代平台上都可用。为了使用CBC模式保护数据的完整性和真实性,建议将计数器(CTR)模式和密码块链消息认证码(CBC-MAC)的技术结合起来,形成所谓的CCM模式(NIST, 2004)。
有关有效块模式的更多信息,请参阅NIST 关于块模式选择的指南。
可预测的初始化向量¶
CBC、OFB、CFB、PCBC、GCM模式需要一个初始化向量(IV)作为密码的初始输入。IV不必保密,但它不应该是可预测的:它应该是随机且对于每条加密消息都是唯一/不可重复的。确保IV是使用密码学安全的随机数生成器生成的。有关IV的更多信息,请参阅Crypto Fail的初始化向量文章。
请注意代码中使用的密码学库:许多开源库在其文档中提供的示例可能遵循不良实践(例如使用硬编码的IV)。一个常见的错误是复制粘贴示例代码而不更改IV值。
对加密和认证使用相同的密钥¶
一个常见的错误是为 CBC 加密和 CBC-MAC 重复使用相同的密钥。通常不建议将密钥用于不同目的,但在 CBC-MAC 的情况下,这种错误可能导致中间人攻击(“CBC-MAC”,2024.10.11)。
有状态操作模式中的初始化向量¶
请注意,当在 CTR 和 GCM 模式中使用 IV 时,其用法是不同的,在这种模式下,初始化向量通常是一个计数器(在 CTR 中与一个随机数结合)。因此,这里使用一个具有其自身有状态模型的可预测 IV 正是所需要的。在 CTR 中,每个新块操作都有一个新的随机数加计数器作为输入。例如:对于一个 5120 位长的明文:您有 20 个块,因此您需要 20 个由随机数和计数器组成的输入向量。而在 GCM 中,每个密码操作只有一个 IV,不应与相同的密钥重复。有关 IV 的更多详细信息和建议,请参阅 NIST 关于 GCM 的文档第 8 节。
由于较弱的填充或分组操作实现导致的填充预言攻击¶
在过去,PKCS1.5 填充(在代码中:PKCS1Padding
)被用作进行非对称加密时的填充机制。这种机制容易受到填充预言攻击。因此,最好使用 PKCS#1 v2.0 中包含的 OAEP (Optimal Asymmetric Encryption Padding)(在代码中:OAEPPadding
, OAEPwithSHA-256andMGF1Padding
, OAEPwithSHA-224andMGF1Padding
, OAEPwithSHA-384andMGF1Padding
, OAEPwithSHA-512andMGF1Padding
)。请注意,即使使用 OAEP,您仍然可能遇到最广为人知的 Manger 攻击,如 Kudelskisecurity 博客中所述。
注意:已证明带有 PKCS #7 的 AES-CBC 也容易受到填充预言攻击,前提是实现会给出警告,例如“填充错误”、“MAC错误”或“解密失败”。有关示例,请参阅填充预言攻击和CBC 填充预言问题。接下来,最好确保在加密明文后添加 HMAC:毕竟,带有失败 MAC 的密文无需解密即可丢弃。
保护存储和内存中的密钥¶
当内存转储是您威胁模型的一部分时,密钥在活动使用时即可被访问。内存转储要么需要 root 权限(例如已 root 或已越狱的设备),要么需要使用 Frida 修补的应用程序(这样您就可以使用诸如 Fridump)之类的工具)。因此,如果设备上仍需要密钥,最好考虑以下几点
- 远程服务器中的密钥:您可以使用远程密钥保管库,例如 Amazon KMS 或 Azure Key Vault。对于某些用例,在应用程序和远程资源之间开发一个编排层可能是一个合适的选择。例如,在函数即服务 (FaaS) 系统(例如 AWS Lambda 或 Google Cloud Functions)上运行的无服务器函数,它将检索 API 密钥或秘密的请求转发。还有其他替代方案,例如 Amazon Cognito、Google Identity Platform 或 Azure Active Directory。
- 安全硬件支持存储中的密钥:确保所有加密操作和密钥本身都保留在可信执行环境(例如使用Android Keystore)或安全飞地(例如使用钥匙串)中。有关更多信息,请参阅Android 数据存储和iOS 数据存储章节。
- 通过信封加密保护的密钥:如果密钥存储在 TEE / SE 之外,请考虑使用多层加密:一种信封加密方法(参见OWASP 密码存储备忘录,Google Cloud 密钥管理指南,AWS 良好架构框架指南),或HPKE 方法,用密钥加密密钥来加密数据加密密钥。
- 内存中的密钥:确保密钥在内存中停留的时间尽可能短,并考虑在成功的密码操作之后以及发生错误时将密钥清零和置空。注意:在某些语言和平台(例如那些具有垃圾回收或内存管理优化的平台)中,可靠地清零内存可能不可行,因为运行时可能会移动或复制内存,或者延迟实际擦除。有关通用密码编码指南,请参阅清除秘密数据内存。
注意:考虑到内存转储的容易性,除了用于签名验证或加密的公钥之外,切勿在账户和/或设备之间共享相同的密钥。
保护传输中的密钥¶
当密钥需要从一个设备传输到另一个设备,或从应用程序传输到后端时,请确保通过传输密钥对或其他机制采取适当的密钥保护措施。通常,密钥是通过混淆方法共享的,这些方法很容易被逆向。相反,请确保使用非对称密码学或包装密钥。例如,对称密钥可以用非对称密钥对的公钥加密。
Android 和 iOS 上的密码学 API¶
虽然无论特定操作系统如何,相同的基本密码学原理都适用,但每个操作系统都有自己的实现和API。平台特定的数据存储密码学API在“Android 上的数据存储”和“iOS 上的数据存储测试”章节中更详细地介绍。网络流量加密,特别是传输层安全(TLS),在“Android 网络 API”章节中介绍。
密码学策略¶
在大型组织中,或当创建高风险应用程序时,通常最好根据诸如NIST 密钥管理建议之类的框架制定密码学策略。当应用程序的密码学应用中发现基本错误时,这可能是建立经验教训/密码学密钥管理策略的一个良好起点。
密码学法规¶
当您将应用程序上传到 App Store 或 Google Play 时,您的应用程序通常存储在美国服务器上。如果您的应用程序包含密码学并分发到任何其他国家,则被视为密码学出口。这意味着您需要遵守美国针对密码学的出口法规。此外,一些国家对密码学有进口法规。
了解更多