如何在iOS / Cocoa应用程序中安全地包含密钥/签名

时间:2010-10-25 13:45:59

标签: objective-c cocoa security ios

我想在iOS应用程序中包含一个密钥,以便应用程序可以向某个服务器“证明”请求来自应用程序本身而不是其他系统。我知道简单地将密钥硬编码到代码本身是非常容易的,因为任何人都可以越狱他们的手机并将GDB附加到我的应用程序的进程以获取密钥。有没有更安全的方法来做到这一点?是否有可能使密钥充分混淆,使其几乎不可能?

我认为这与序列号验证类似。不幸的是,这似乎经常被轻易破解。这有什么解决方案吗?

与我的服务器的所有通信都将通过HTTPS完成,因此至少在中间攻击中嗅探/骚扰不应该是一个问题。

谢谢,M

7 个答案:

答案 0 :(得分:20)

我也一直想知道这个问题,并且基于这样一个前提,我想到了几个可能的解决方案:你想要的是将用户/密钥传递到你的应用程序的KeyChain(这是由iOS和硬件)并根据需要拉出它:

  1. 使用特定于应用的iCloud ubiquity-container将秘密分发到您的应用。这些数据应该从备份到本地计算机中排除,据称使用硬件级安全性安全地传输到非越狱应用程序。专业人士:它不是在您的应用程序初始分发,因此更难发现,iCloud需要一个非越狱设备,您可以更新您的秘密,它将同步到您的所有应用程序。 con:它不是真正的安全KeyChain,这意味着如果iCloud同步,然后设备被越狱,它可能会在文件系统上被嗅出。

  2. 将这个秘密传递给您的应用,作为免费应用商店托管的应用内购买内容。当它(通过应用商店安全地,仅通过非越狱设备)传送到应用程序时,将其传送到钥匙串。专业人士:它不是在您的应用程序初始分发,因此难以发现,app-store需要一个非越狱设备。 con:更难以快速更改所有安装的秘密,即使购买免费的App-store也可能需要用户身份验证,这很麻烦。

  3. 理想的解决方案是,当我们提交分发时,我们可以以某种方式将秘密(KeyChain键/值字典)捆绑到应用程序中,应用程序商店将剥离这些并将它们安全地传送到操作系统以便注入安装过程中的KeyChain,但是与桌面计算机和iTunes同步的普通应用程序包的带外,它们不会出现在二进制文件中。除非Apple添加这样的功能,我认为没有真正可靠的解决方案。

答案 1 :(得分:5)

我担心这样做是不可能的。但据我所知,苹果将确保没有其他应用程序欺骗你的应用程序的秘密。如果是越狱手机,那么用户将承担全部责任,可能的损害应仅限于越狱手机用户的数据。

答案 2 :(得分:3)

由于攻击者可以完全控制客户端,唯一的方法是通过默默无闻的安全性。您可以实施挑战/响应模型,但必须使其不受许多其他因素(重放攻击等)的影响。

This question包含一种以二进制代码隐藏密钥的方法。

不要以为只使用https就不会有数据包嗅探。如果攻击者将可执行文件中的URL更改为指向其服务器,该怎么办?然后他们可以充当接力。

更好的方法是在应用程序内部提供一些身份管理(用户选择用户名,密码是用户/机器生成的),并使用这些凭据进行服务调用。

答案 3 :(得分:3)

我同意@Nubis的说法,没有100%的防弹方法。

但是,这个库似乎是解决问题的实用方法:

https://github.com/UrbanApps/UAObfuscatedString

它可能无法让你从一个积极主动的攻击者身上拯救你, 但这不会让他们的生活轻松。

答案 4 :(得分:1)

作为@natbro答案的更新,现在一个很好的解决方案是使用CloudKit。使用这种方法,您可以在公共数据库中创建一条记录,并且该应用程序的每个实例在启动时都会抓取该记录。由于CloudKit基于iCloud登录,因此它具有与特定于应用程序的iCloud泛型容器所具有的大多数(即使不是全部)相同的保护措施。主要区别有两个:

  1. CloudKit在检索数据时更具确定性。您知道当您从CloudKit中获取机密/密钥时,这些机密/密钥将可用。

  2. CloudKit中的数据不会同步到设备或缓存在应用程序的容器中,而是按需检索,并且任何缓存都取决于开发人员(请注意:我建议将密钥缓存在钥匙串)。

这是从CloudKit检索记录的快速摘要。

import CloudKit

...

let publicCloudKitDatabase = CKContainer.default().publicCloudDatabase
let recordID = CKRecord.ID(recordName: "default") // or whatever you name it

publicCloudKitDatabase.fetch(withRecordID: recordID) { (record, error) in
    if let secretRecord = record {
        guard let secret = secretRecord["aKey"] as? String else {
            print("Unable to get secret")
            return
        }

        self.secret = secret // or somesuch
    }
}

注意:您需要将CloudKit设置为specified in the docs,然后创建一条与您的应用期望的记录相匹配的记录,反之亦然(在这种情况下,具有recordName =“ default”的记录包含一个字段使用键“ aKey”)。

答案 5 :(得分:0)

通常的答案是实施“握手”协议,其中服务器发送“质询”并且客户端必须提供有效的答案。

这比硬编码答案更安全,但需要智能算法(例如避免使用标准哈希)。

答案 6 :(得分:-2)

如果您对应用程序内的密钥进行硬编码,则有更多机会破解它,因此如果应用程序每次向服务器发送请求并从服务器接收密钥会更好。