iOS应用程序中的安全性

下午好,哈伯! 我提供给您的翻译是Arlind Aliu撰写的有关iOS应用程序中机密数据安全的基本原理的文章“每个iOS应用程序的应用程序安全必备

应用程序安全性是软件开发最重要的方面之一。 应用程序用户希望他们提供的信息受到安全保护。 因此,您不仅可以向他人提供机密信息。

幸运的是,在本文中,我们将讨论开发人员在其应用程序中所犯的错误,以及解决这些错误的方法。
继续下割。

数据存储在错误的地方


我对AppStore中的几个应用程序进行了研究,许多应用程序都犯了同样的错误:机密信息存储在不应存储的位置。
如果您将个人数据存储在UserDefaults中 ,那么您将面临风险。 UserDefaults存储在带有属性列表的文件中,该文件位于应用程序的“ Settings”文件夹中。 数据存储在应用程序中,没有丝毫加密提示。

通过在Mac上安装第三方程序(例如iMazing),您甚至不必破解手机,但可以立即从AppStore安装的应用程序中查看所有UserDefaults数据。 此类程序可让您查看和管理来自iPhone上安装的应用程序的数据。 您可以轻松获取任何应用程序的UserDefaults
这是我决定写文章的主要原因-我在AppStore中找到了许多将数据存储在UserDefaults中的应用程序,例如:令牌,活动和可更新订阅,可用金额等等。 从管理应用程序中的付费订阅到网络级别的黑客攻击,甚至可以恶意地轻易获取并使用所有这些数据。

现在介绍如何存储数据。

请记住,只有少量信息应存储在UserDefaults中 ,例如应用程序内的设置,即对用户不保密的数据。

使用Apple专用的安全服务存储个人信息。 钥匙串API服务允许您在加密的数据库中存储一定数量的用户数据。 您可以在其中存储用户的密码和其他重要数据,例如信用卡信息,甚至是重要的小提示。
另外,可能还有与您一起使用的加密密钥和证书。

钥匙串API服务


以下是如何在钥匙串中保存用户密码的示例。

class KeychainService { func save(_ password: String, for account: String) { let password = password.data(using: String.Encoding.utf8)! let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecValueData as String: password] let status = SecItemAdd(query as CFDictionary, nil) guard status == errSecSuccess else { return print("save error") } } 

kSecClass词典的一部分:kSecClassGenericPassword表示需要加密的信息是密码。 然后,我们通过调用SecItemAdd方法将新密码添加到钥匙串中。 从包中检索数据类似于保存。

 func retrivePassword(for account: String) -> String? { let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecMatchLimit as String: kSecMatchLimitOne, kSecReturnData as String: kCFBooleanTrue] var retrivedData: AnyObject? = nil let _ = SecItemCopyMatching(query as CFDictionary, &retrivedData) guard let data = retrivedData as? Data else {return nil} return String(data: data, encoding: String.Encoding.utf8) } 

让我们来简单检查一下保存和接收数据的正确性。

 func testPaswordRetrive() { let password = "123456" let account = "User" keyChainService.save(password, for: account) XCTAssertEqual(keyChainService.retrivePassword(for: account), password) } 

乍一看,钥匙串API似乎很难使用,尤其是在您需要保存多个密码的情况下,因此,我敦促您将Facade模式用于这些目的。 它将允许您根据应用程序的需要保存和修改数据。

如果您想进一步了解这种模式,以及如何为复杂的子系统创建简单的包装器,那么本文将对您有所帮助。 互联网上也有许多开放库,可以帮助使用Keychain API,例如SAMKeychainSwiftKeychainWrapper

密码保存和授权


在我的发展生涯中,我经常遇到同样的问题。 开发人员要么将密码存储在应用程序中,要么向服务器创建请求,服务器直接发送用户名和密码。

如果将数据存储在UserDefault中 ,那么在阅读了文章第一部分的信息之后,您已经了解了要冒多少风险。 通过将密码存储在钥匙串中,可以大大提高应用程序的安全级别,但是同样,在将敏感信息保存到任何地方之前,必须首先对其进行加密。

假设黑客可以通过我们的网络攻击我们。 因此,他将以原始文本形式接收密码。 当然,最好对所有密码进行哈希处理。

个人资料加密


如果您自己进行操作,则散列似乎有点不堪重负 ,因此在本文中,我们将使用CryptoSwift库。 它收集了许多Swift中使用的标准可靠加密算法。

让我们尝试使用CryptoSwift算法从钥匙串中保存和检索密码。

 func saveEncryptedPassword(_ password: String, for account: String) { let salt = Array("salty".utf8) let key = try! HKDF(password: Array(password.utf8), salt: salt, variant: .sha256).calculate().toHexString() keychainService.save(key, for: account) } 

上面的函数记录了用户名和密码,并将它们作为加密字符串保存在钥匙串中。

让我们看看里面发生了什么:

-登录名和密码作为字符串写入salt变量
-sha256填充SHA-2哈希
-HKDF是基于消息身份验证代码( HMAC )的密钥生成功能( KDF

我们创建了salt变量以使黑客的任务复杂化。 我们只能对密码进行加密,但是在这种情况下,攻击者可以拥有最常用密码的列表,他会毫无问题地对其进行加密,并将其与我们的加密密码进行比较。 然后查找特定帐户的密码并不困难。
现在,我们可以使用我们的帐户和生成的密钥登录。

 authManager.login(key, user) 

当然,服务器应该知道我们的salt变量中加密了什么。 后端将能够使用相同算法比较密钥以识别用户。
使用这种方法将大大增强应用程序的安全性。

作为完成


永远不要忽视应用程序的安全性。 在本文中,我们首先弄清楚了在UserDefaults中存储敏感数据时可能产生的后果以及为什么需要Keychain。

在第二部分中,我们将讨论更高级别的安全性,即在保存数据之前先对其进行加密,并讨论如何将个人数据与信息正确地传输到服务器。

Source: https://habr.com/ru/post/zh-CN430532/


All Articles