如何在iOS中安全地保存字符串?

时间:2017-07-23 16:19:07

标签: ios passwords keychain

我必须在钥匙串中保存一个字符串(密码),但原始字符串是

  1. 不是来自服务器;
  2. 不是由用户生成的;
  3. 必须在服务器上进行比较(我通过https发送);
  4. 那么,字符串必须存在于app中的某个位置(硬编码?)。

    我非常确定您无法为安装后的应用程序准备好钥匙串的数据,就像您可以在应用程序包中添加一个plist,以便可以立即加载它应用程序正在运行(即使它是第一次启动)。

    我读过Data Protection:它允许使用敏感数据的应用程序利用某些设备上的加密功能。这是要走的路吗?那就是:我将数据存储到文本文件中,然后保护文件,然后从文件中检索数据,然后将其保存到钥匙串中?

    任何提示都表示赞赏。

6 个答案:

答案 0 :(得分:2)

如果无法从外部提供字符串(用户输入,服务器),则开发人员必须以某种形式将其放入应用程序包中。它可以是硬编码的,存储在文件中,也可以由函数生成。这意味着应用程序具有获取/生成此类字符串所需的所有信息。 因此,它无法像加密一样保护信息

iOS应用程序受Apple的DRM保护,因此如果有人将您的二进制文件复制到Mac并开始反汇编,您会感到安全。但是,如果黑客有一个越狱设备,不幸的是,有一些工具可以将你的应用程序二进制文件从内存转储到磁盘。

所以它归结为混淆。您可以编写一个动态生成字符串的函数(例如,对硬编码字节数组执行一系列操作,然后将其转换为字符串)。这将使你的字符串更难被黑客拦截。

答案 1 :(得分:2)

这个问题的变种已经在很多时候讨论过了,不幸的是你没有找到一个完美的答案,因为不是

基本上,您希望在应用中发送身份验证凭据,即将秘密捆绑到其中。这意味着无论您做什么,攻击者都有可能通过逆向工程检索它​​。即使您使用技术上足够安全的硬算法对其进行加密,在某些时候您的应用程序也会对其进行解密,并且由于应用程序可能掌握在攻击者手中,因此他们将能够嗅探秘密。

这意味着最终你只能通过基本上混淆你处理秘密的方式("使嗅探变得复杂")来试图让它们变得困难。捆绑一个加密的文件,解密然后打包到钥匙串对我来说似乎不是一个坏主意,但请记住,对于有人看着你的应用程序,特别是在越狱的iPhone上,这个消失的文件可以是一个好的第一个提示。此外,删除它并没有什么帮助,因为重新安装很容易恢复它。

另一个想法可能是使用On-Demand Resources来分发您的秘密,这也可能使您更容易用更新版本替换它,以防您的秘密被泄露。我自己并不熟悉按需资源,所以我无法告诉你它们对实际撤销事物的适用程度。

这都假定您无法根据用户输入实现身份验证机制。这样攻击者只能窃取自己的密码(假设他们不会窃取别人的iPhone ......)而不是整个应用程序的重要部分(可能会影响所有用户)。

我的书签中的其他SO答案可能会对您有所帮助:

好的,在重新阅读了问题的最后一条评论之后,这是一个具体的建议。如果你已经拥有用户身份验证(你说他们已经登录了),我根本不知道为什么你需要这个第二个密码/令牌,但是你现在就去了:

  1. 您根本不会将令牌/通行证与应用程序捆绑在一起。首次运行您的应用时,用户必须以任何方式登录(如果我找到了您的权利),那么您的服务器就可以参与其中,这就是您开始使用的地方。
  2. 在此登录期间,服务器会生成令牌/密码并将其发送给用户(具体取决于您的确切实施/自定义协议,这可能是在第二次请求/响应中完成的使用相同用户凭据的应用程序,或者将其放入应用程序发送的第一个请求的响应标题中。请注意,只要您使用https,就不需要加密,因为这已经加密了这些数据。因此,令牌在您的应用中到达的渠道是安全的。
  3. 应用程序然后立即将此令牌/传递保存在钥匙串中。这与移动设备上的加密一样好。不要将令牌保存在其他任何内容中,即使是暂时的。只有在记忆中它才会存在(你无法避免)。
  4. 您可以将该令牌用于您需要的任何内容(如果您已经使用过https和用户登录系统,我实际上想知道为什么需要它。)
  5. 总而言之,如果您只是想使用令牌以避免在每个请求中始终依赖用户凭据(用户名和密码),那么这听起来就像是一种非常标准的伪OAuth令牌方法。从技术上讲,您也可以只在密钥链中保存用户名和密码,并始终为每个请求获取。通常这些令牌的生存时间有限,之后无论如何它们都会变得无效,并且应用程序必须再次依赖用户名和密码才能获得新的令牌。

    令牌的潜在缺点是它是静态的而不是用户限制的,或者它是否没有这个生存时间限制。您必须在服务器上以巧妙的方式生成此内容,并明确记下哪个用户使用哪个令牌。这样,您就可以查明安全漏洞并做出相应的反应(而不是通过使您的唯一令牌失效来突然关闭所有用户的服务器)。不过,我不知道为什么你需要这个。

    关于中间人攻击:一个令牌,无论是app-bundled(这本身就是一种风险)还是服务器生成的,它本身并不能防止这种情况发生。我不知道你的老板会在这里瞄准什么,但也许我错过了一些信息。一般来说,https已经保护你免受这种情况(好吧,通常只有服务器经过身份验证,但如果你已经有了用户名和密码系统,那么对于客户端来说也应该没问题)。总的来说,它实际上是一个重点。对我而言,这听起来越来越像原始问题中的某些东西,只是对现有基础设施和/或老板诱导的"的误解。问题......:)

答案 2 :(得分:2)

主要关注的是复述或访问令牌,它可以作为加密数据保存在钥匙串中,但每当我们发送请求时,它都会被解密。它可以在越狱设备上进行跟踪。但是使用更多不可知算法加密请求数据会增加API响应时间。但是,在服务器端定义基于用户的权限是最好的方法,因为在最坏的情况下,只能跟踪1个用户的数据。

SSL Pinning - 使用质询响应身份验证,我们可以防止app在中间攻击中受到攻击。是的,可以绕过ssl pinning,但这并不容易。只能在越狱设备上使用。

将到期时间定义为几秒可能是防止用户数据的另一种方法。在授予权限之前,识别请求源也非常重要。 因此,通过多种方式的合作,我们可以尝试建立更好的系统。

修改

通常我们使用1个密钥作为令牌和放大器加密数据。一旦该密钥被泄露,它就可以被解密。 非对称加密使用公共和加密增加了1层加密。私钥。 https://developer.apple.com/library/content/documentation/Security/Conceptual/Security_Overview/CryptographicServices/CryptographicServices.html#//apple_ref/doc/uid/TP30000976-CH3-SW12

这是另一个例子,其中随机盐用于令牌以编码数据https://en.wikipedia.org/wiki/PBKDF2

答案 3 :(得分:2)

您可以做的是在调用API时使用AES 256加密对参数进行加密,然后服务器解密参数并再次发送加密响应。如果您的服务器和应用程序共享相同的密钥,您可以解密并读取响应用于加密和解密。

我的应用程序中有类似功能,因此我的Util类使用以下代码加密和解密请求和响应,

   class func encrypt (stringToEncrypt: String) -> String {
        let messageData = stringToEncrypt.data(using: .utf8)
        let encryptedBytes = try! AES(key: "abcdefghijklmnopqrstuvwxyz012345", iv: "abcdefghijklmost").encrypt([UInt8](messageData!))
        return encryptedBytes.toBase64()!
    }

   class func decrypt ( message: String) -> String {
        let messageData = Data(base64Encoded: message, options: .ignoreUnknownCharacters)
        let decryptedBytes: [UInt8] = try! AES(key: "abcdefghijklmnopqrstuvwxyz012345", iv: "abcdefghijklmost").decrypt([UInt8](messageData!))
        let unencryptedString = String(bytes: decryptedBytes, encoding: .utf8)
        return unencryptedString!
    } 

同样,这只是一个建议,你也可以用其他方式来做。

答案 4 :(得分:1)

要在Keychain中保存一些字符串值,您可以使用pod库

pod 'SSKeychain'

您可以将字符串保存到钥匙串,如下所示

let appName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
SSKeychain.setAccessibilityType(kSecAttrAccessibleAlways)
SSKeychain.setPassword(stringToSave, forService: appName, account: "MyAppName")

也可以使用

检索相同内容
let appName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
let stringRetrieved = SSKeychain.passwordForService(appName, account: "MyAppName")

安装上述窗格

后导入这些内容
import SystemConfiguration
import SSKeychain

即使应用程序已删除并重新安装,也会保留此项。如果您想要额外的加密,可以使用任何加密算法,这个算法非常有用

AES encryption in swift

答案 5 :(得分:1)

加密技术可能有助于解决需求。以下是我们如何在Diffie–Hellman key exchange加密技术之上维护密钥的简单阐述。

<强>先决条件:

  1. 维护客户端和服务器端的公钥和私钥对。
  2. 生成私钥的算法或逻辑应该对客户端和服务器端使用相同的。
  3. 打破最终获取键以比较自己的字符串的逻辑。
  4. <强>过程:

    1. 应用程序和后端都应该有一个公共公钥,在这种情况下只需将其作为应用程序名称,如MyApplication。

      public key: MyApplication

    2. 应使用某些加密过程或逻辑在两端生成私钥。假设将随机数生成为私钥,如下所示。

      Suppose App private key is: 10 Suppose Backend private key is: 90

    3. 按照某种算法在app和后端生成交换密钥。这里只是结合公钥和私钥,如

      App final key: MyApplication + 10 Backend final key: MyApplication + 90

    4. 在app和backend之间交换密钥。

      App Got key: MyApplication + 90 Backend Got key: MyApplication + 10

    5. 使用某种技术(如

      )将收到的密钥与自己的密钥进行比较

      我。通过组合应用私钥和App Got键生成新的App键,如:MyApplication + 90 + 10

      II。通过组合Backend私钥和Backend Got键生成新的后端键,如:MyApplication + 10 + 90

    6. 要检查两个键是否相同需要遵循逻辑例如这里只需在键中添加最后两个数字结果就像

      App owned key: MyApplication + 100

      App acquired key: MyApplication + 100

      BackEnd owned key: MyApplication + 100

      BackEnd acquired key: MyApplication + 100

    7. Hola终于后端和app都有相同的密钥。

      取决于需求需求,每个会话维护不同的私钥并将其存储在钥匙串上。或制作一个私钥并将其存储在应用程序端。

      注意:此描述仅概述了如何维护密钥,它可能涉及大量逻辑,并且还提供了完全依赖于使用加密和逻辑的其他人分解密钥的规定生成并破坏密钥。

      <强>参考文献:

      1. https://en.wikipedia.org/wiki/Diffie-Hellman_key_exchange
      2. https://security.stackexchange.com/questions/7390/how-to-properly-encrypt-a-communication-channel-between-a-client-and-a-server-w