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

Posted

技术标签:

【中文标题】如何在 iOS/Cocoa 应用程序中安全地包含密钥/签名【英文标题】:How to securely include secret key/signature in iOS/Cocoa apps 【发布时间】:2011-04-30 05:35:39 【问题描述】:

很久以前我就知道这是个坏主意。请不要尝试我在下面要求的内容

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

我相信这是一个与序列号验证类似的问题。不幸的是,这似乎经常被轻易破解。有什么解决办法吗?

与我的服务器的所有通信都将通过 HTTPS 完成,因此至少无需担心嗅探/中间人攻击。

谢谢,M

【问题讨论】:

【参考方案1】:

我也一直想知道这一点,并且基于以下前提,我想到了几个潜在的解决方案:您想要将用户/密码密钥放入应用程序的 KeyChain(iOS 和硬件)并根据需要拉取使用:

    使用特定于应用程序的 iCloud 通用容器将密钥分发到您的应用程序。此数据应从备份到本地计算机中排除,据称仅使用硬件级别的安全性将其安全地传输到未越狱的应用程序。专业人士:最初分发时它不在您的应用程序中,因此更难发现,iCloud 需要未越狱的设备,您可以更新您的秘密,它将同步到您的所有应用程序。缺点:它实际上并不在安全的 KeyChain 中,这意味着如果 iCloud 同步然后设备被越狱,它可能会在文件系统上被嗅出。

    将秘密作为免费的应用商店托管的应用内购买内容提供给您的应用。当它被交付(由应用商店安全地,仅限于非越狱设备)到应用程序时,将其转移到钥匙串中。专业人士:最初分发时它不在您的应用程序中,因此更难发现,应用商店需要未越狱的设备。缺点:很难快速更改所有安装的密码,即使是免费的应用商店购买也可能需要用户身份验证,这对用户体验来说很麻烦。

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

15 年 6 月更新:在 iOS(和 OS X)钥匙串的安全性惊人严重缺陷得到解决之前(请参阅Unauthorized Cross-App Resource Access on MAC OS X and iOS),您不能使用钥匙串来合理地保护任何东西.不要使用它。

【讨论】:

这很有帮助,natbro! iOS 7 有更新吗? 我还没有找到任何其他途径来简单地在 iOS7 中执行此操作(但没有深入研究)。由于 iCloud 的脆弱性,我确实发现在第一次运行时使用 iCloud 分发这种数据可能会很尴尬。一旦你获得了价值,它就是一个很好的解决方案,但你不能确定它会在多长时间内到达设备。真希望苹果能添加一些东西来支持这一点。 将私钥存储到 iCloud 容器中的方法是什么? 可能还值得考虑通过初始静默推送通知 (ios 8+) 传递共享密钥。显然,如果用户禁用了应用程序的通知,那么你就不走运了。 WWDC 2014 session: 713 讨论了更多的话题。我相信默认情况下会接受静默通知,但用户可以通过禁用后台应用刷新来禁用设置应用中的通知。更多信息:urbanairship.com/blog/watch-list-ios-8-opt-in-changes。如果秘密数据尚未到达,显然需要确保以某种方式交付并提供适当的视觉消息。【参考方案2】:

恐怕这不可能。但据我所知,苹果会确保没有其他应用程序欺骗您的应用程序的秘密。如果是越狱手机,那么用户在某种程度上要承担全部责任,可能的损害应该仅限于越狱手机用户的数据。

【讨论】:

+1 表示“ 没有办法保护这样的事情。”如果您将“秘密”放入应用程序中,那么它就不是秘密了。就像狗遮住骨头一样。稍微挖掘一下就会发现它……尤其是在草看起来很有趣的地方。 对不起,我的意思是“模式”而不是“秘密”,小失误。显然,OP无论如何都理解:)【参考方案3】:

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

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

不要假设仅使用 https 就不能进行数据包嗅探。如果攻击者在您的可执行文件中更改了他们的 URL 以指向他们的服务器怎么办?然后他们可以充当中继。

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

【讨论】:

【参考方案4】:

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

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

https://github.com/UrbanApps/UAObfuscatedString

它可能无法将您从积极主动的攻击者手中拯救出来, 但这不会让他们的生活变得轻松。

【讨论】:

【参考方案5】:

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

    CloudKit 在检索数据时更具确定性。当您从 CloudKit 获取密钥/密钥时,您就知道它们是可用的。

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

这是从 CloudKit 检索记录的快速 sn-p。

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,然后创建一个与您的应用所期望的匹配的记录,反之亦然(在这种情况下,记录名称 = "default" 的记录包含一个带有键“aKey”)。

【讨论】:

【参考方案6】:

通常的答案是实现“握手”协议,服务器发送“挑战”,客户端必须提供有效的答案。

这比硬编码答案提供了更多的安全性,但需要一个智能算法(例如避免标准哈希)。

【讨论】:

【参考方案7】:

如果你在App内部硬编码密钥,就有更多的机会破解它,所以如果App每次都向服务器发送请求并从服务器接收密钥会更好。

【讨论】:

client_secret 用于向服务器验证客户端。那么,如何阻止恶意客户端向服务器请求密钥呢?

以上是关于如何在 iOS/Cocoa 应用程序中安全地包含密钥/签名的主要内容,如果未能解决你的问题,请参考以下文章

安全地存储密码,但确定相同的密码

如何在将文件包含在 PHP 中之前安全地评估文件的内容

NodeJS(Express框架)实现 Token 验证免密登录 (一)

Linux中实现Hadoop各节点间的SSH免密登录

iOS Cocoa - 自定义 uipickerview 或类似

信息安全技术及应用 常规加密技术